From 4c4261be6ceb54109589692f4f18a91b52a5c71c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Sep 2018 08:48:43 +0200 Subject: [PATCH 001/335] Initialized netx plugin --- .../tools/netx/db_plugin/dbNetExtractor.cc | 45 +++++++++++++++ .../tools/netx/db_plugin/dbNetExtractor.h | 56 +++++++++++++++++++ .../netx/db_plugin/dbNetExtractorPlugin.cc | 29 ++++++++++ .../tools/netx/db_plugin/db_plugin.pro | 14 +++++ .../netx/db_plugin/gsiDeclDbNetExtractor.cc | 39 +++++++++++++ src/plugins/tools/netx/netx.pro | 11 ++++ .../netx/unit_tests/dbNetExtractorTests.cc | 30 ++++++++++ .../tools/netx/unit_tests/unit_tests.pro | 20 +++++++ 8 files changed, 244 insertions(+) create mode 100644 src/plugins/tools/netx/db_plugin/dbNetExtractor.cc create mode 100644 src/plugins/tools/netx/db_plugin/dbNetExtractor.h create mode 100644 src/plugins/tools/netx/db_plugin/dbNetExtractorPlugin.cc create mode 100644 src/plugins/tools/netx/db_plugin/db_plugin.pro create mode 100644 src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc create mode 100644 src/plugins/tools/netx/netx.pro create mode 100644 src/plugins/tools/netx/unit_tests/dbNetExtractorTests.cc create mode 100644 src/plugins/tools/netx/unit_tests/unit_tests.pro diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc new file mode 100644 index 000000000..584a7583c --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc @@ -0,0 +1,45 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetExtractor.h" +#include "tlLog.h" + +namespace db +{ + +NetExtractor::NetExtractor() +{ + + // @@@ + +} + +void NetExtractor::dummy() +{ + // @@@ + tl::log << "@@@ this is net extractor!"; + // @@@ +} + +} + diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.h b/src/plugins/tools/netx/db_plugin/dbNetExtractor.h new file mode 100644 index 000000000..64f107b1e --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.h @@ -0,0 +1,56 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbNetExtractor +#define HDR_dbNetExtractor + +#include "dbPluginCommon.h" + +namespace db +{ + +/** + * @brief The net extractor + * + * ... + */ +class DB_PLUGIN_PUBLIC NetExtractor +{ +public: + /** + * @brief Constructs a net extractor + */ + NetExtractor (); + + // @@@ + void dummy (); + +private: + // @@@ +}; + +} + +#endif + diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractorPlugin.cc b/src/plugins/tools/netx/db_plugin/dbNetExtractorPlugin.cc new file mode 100644 index 000000000..f06459c5b --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractorPlugin.cc @@ -0,0 +1,29 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 + +*/ + + +namespace db +{ + + // @@@ + +} diff --git a/src/plugins/tools/netx/db_plugin/db_plugin.pro b/src/plugins/tools/netx/db_plugin/db_plugin.pro new file mode 100644 index 000000000..9126b6af1 --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/db_plugin.pro @@ -0,0 +1,14 @@ + +TARGET = netx +DESTDIR = $$OUT_PWD/../../../../db_plugins + +include($$PWD/../../../db_plugin.pri) + +HEADERS = \ + dbNetExtractor.h \ + +SOURCES = \ + dbNetExtractor.cc \ + dbNetExtractorPlugin.cc \ + gsiDeclDbNetExtractor.cc \ + diff --git a/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc new file mode 100644 index 000000000..ef2b53845 --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc @@ -0,0 +1,39 @@ +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetExtractor.h" + +#include "gsiDecl.h" + +namespace gsi +{ + +gsi::Class decl_NetNetExtractor ("db", "NetExtractor", + gsi::method ("dummy", &db::NetExtractor::dummy, + "@@@" + ), + "@brief The net extractor\n" + "\n" + "This class has been introduced in version 0.26." +); + +} diff --git a/src/plugins/tools/netx/netx.pro b/src/plugins/tools/netx/netx.pro new file mode 100644 index 000000000..f42b2a627 --- /dev/null +++ b/src/plugins/tools/netx/netx.pro @@ -0,0 +1,11 @@ + +TEMPLATE = subdirs + +SUBDIRS = db_plugin unit_tests +unit_tests.depends += db_plugin + +#!equals(HAVE_QT, "0") { +# SUBDIRS += lay_plugin +# lay_plugin.depends += db_plugin +#} + diff --git a/src/plugins/tools/netx/unit_tests/dbNetExtractorTests.cc b/src/plugins/tools/netx/unit_tests/dbNetExtractorTests.cc new file mode 100644 index 000000000..28ce60281 --- /dev/null +++ b/src/plugins/tools/netx/unit_tests/dbNetExtractorTests.cc @@ -0,0 +1,30 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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" + +TEST(1) +{ + // .. nothing yet .. +} + diff --git a/src/plugins/tools/netx/unit_tests/unit_tests.pro b/src/plugins/tools/netx/unit_tests/unit_tests.pro new file mode 100644 index 000000000..ac1f944ea --- /dev/null +++ b/src/plugins/tools/netx/unit_tests/unit_tests.pro @@ -0,0 +1,20 @@ + +DESTDIR_UT = $$OUT_PWD/../../../.. + +TARGET = net_tracer_tests + +include($$PWD/../../../../lib_ut.pri) + +SOURCES = \ + dbNetExtractor.cc \ + +INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common +DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common + +LIBS += -L$$DESTDIR_UT -lklayout_db -lklayout_tl -lklayout_gsi + +# This makes the test pull the mebes library for testing (not installed) +PLUGINPATH = $$OUT_PWD/../../../../db_plugins +QMAKE_RPATHDIR += $$PLUGINPATH + +LIBS += -L$$PLUGINPATH -lnetx From b25401c254b06c8bac4a741106918fd37d43f56d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 20 Sep 2018 23:44:51 +0200 Subject: [PATCH 002/335] Added twofold-typed box scanner. --- src/db/db/dbBoxScanner.h | 371 +++++++++++++++++++++++++++++- src/db/unit_tests/dbBoxScanner.cc | 247 ++++++++++++++++++++ 2 files changed, 617 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index 52423afe0..4d6ee0f70 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -122,7 +122,7 @@ struct box_scanner_receiver */ void finish (const Obj * /*obj*/, const Prop & /*prop*/) { } - /* + /** * @brief Callback for an interaction of o1 with o2. * * This method is called when the object o1 interacts with o2 within the current @@ -389,6 +389,375 @@ private: std::string m_progress_desc; }; +/** + * @brief A template for the twofold box scanner output receiver + * + * This template specifies the methods or provides a default implementation for them + * for use as the output receiver of the twofold box scanner. + */ +template +struct box_scanner_receiver2 +{ + /** + * @brief Indicates that the given object of first type is no longer used + * + * The finish1 method is called when an object of the first type is no longer in the queue and can be + * discarded. + */ + void finish1 (const Obj1 * /*obj*/, const Prop1 & /*prop*/) { } + + /** + * @brief Indicates that the given object of second type is no longer used + * + * The finish method is called when an object of the second type is no longer in the queue and can be + * discarded. + */ + void finish2 (const Obj2 * /*obj*/, const Prop2 & /*prop*/) { } + + /** + * @brief Callback for an interaction of o1 with o2. + * + * This method is called when the object o1 interacts with o2 within the current + * definition. + */ + void add (const Obj1 * /*o1*/, const Prop1 & /*p1*/, const Obj2 * /*o2*/, const Prop2 & /*p2*/) { } +}; + +/** + * @brief A box scanner framework (twofold version) + * + * This implementation provides a box scanner for two different types. Apart from + * that it is similar to the uniform-type box scanner. + * + * It will not report interactions within the Obj1 or Obj2 group, but only + * interactions between Obj1 and Obj2 objects. + */ +template +class box_scanner2 +{ +public: + typedef Obj1 object_type1; + typedef Obj2 object_type2; + typedef std::vector > container_type1; + typedef std::vector > container_type2; + typedef typename container_type1::iterator iterator_type1; + typedef typename container_type2::iterator iterator_type2; + + /** + * @brief Default ctor + */ + box_scanner2 (bool report_progress = false, const std::string &progress_desc = std::string ()) + : m_fill_factor (2), m_scanner_thr (100), + m_report_progress (report_progress), m_progress_desc (progress_desc) + { + // .. nothing yet .. + } + + /** + * @brief Sets the scanner threshold + * + * This value determines for how many elements the implementation switches to the scanner + * implementation instead of the plain element-by-element interaction test. + * The default value is 100. + */ + void set_scanner_threshold (size_t n) + { + m_scanner_thr = n; + } + + /** + * @brief Gets the scanner threshold + */ + size_t scanner_threshold () const + { + return m_scanner_thr; + } + + /** + * @brief Sets the fill factor + * + * The fill factor determines how many new entries will be collected for a band. + * A fill factor of 2 means that the number of elements in the band will be + * doubled after elements outside of the band have been removed. + * The default fill factor is 2. + */ + void set_fill_factor (double ff) + { + m_fill_factor = ff; + } + + /** + * @brief Gets the fill factor + */ + double fill_factor () const + { + return m_fill_factor; + } + + /** + * @brief Reserve for n elements of Obj1 type + */ + void reserve1 (size_t n) + { + m_pp1.reserve (n); + } + + /** + * @brief Reserve for n elements of Obj2 type + */ + void reserve2 (size_t n) + { + m_pp2.reserve (n); + } + + /** + * @brief Clears the container + */ + void clear () + { + m_pp1.clear (); + m_pp2.clear (); + } + + /** + * @brief Inserts a new object of type Obj1 into the scanner + * + * The object's pointer is stored, so the object must remain valid until the + * scanner does not need it any longer. An additional property can be attached to + * the object which will be stored along with the object. + */ + void insert1 (const Obj1 *obj, const Prop1 &prop) + { + m_pp1.push_back (std::make_pair (obj, prop)); + } + + /** + * @brief Inserts a new object of type Obj2 into the scanner + * + * The object's pointer is stored, so the object must remain valid until the + * scanner does not need it any longer. An additional property can be attached to + * the object which will be stored along with the object. + */ + void insert2 (const Obj2 *obj, const Prop2 &prop) + { + m_pp2.push_back (std::make_pair (obj, prop)); + } + + /** + * @brief Get the interactions between the stored objects + * + * Two objects interact if the boxes of the objects enlarged by the given value overlap. + * The enlargement is specified in units of width and height, i.e. half of the enlargement + * is applied to one side before the overlap check. + * + * An enlargement of 1 means that boxes have to touch only in order to get an interaction. + * + * The box scanner will report all interactions of type Obj1 and Obj2 objects to the receiver object. + * See box_scanner_receiver2 for details about the methods that this object must provide. + * + * The box converter 1 must be capable of converting the Obj1 object into a box. + * It must provide a box_type typedef. The box converter 2 must be able to convert Obj2 to + * a box. The box type of both box converters must be identical. + */ + template + void process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ()) + { + typedef typename BoxConvert1::box_type box_type; // must be same as BoxConvert2::box_type + typedef typename box_type::coord_type coord_type; + typedef bs_side_compare_func > bottom_side_compare_func1; + typedef bs_side_compare_func > left_side_compare_func1; + typedef bs_side_compare_vs_const_func > below_func1; + typedef bs_side_compare_vs_const_func > left_func1; + typedef bs_side_compare_func > bottom_side_compare_func2; + typedef bs_side_compare_func > left_side_compare_func2; + typedef bs_side_compare_vs_const_func > below_func2; + typedef bs_side_compare_vs_const_func > left_func2; + + if (m_pp1.empty () || m_pp2.empty ()) { + + // trivial case + + for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) { + rec.finish1 (i->first, i->second); + } + for (iterator_type2 i = m_pp2.begin (); i != m_pp2.end (); ++i) { + rec.finish2 (i->first, i->second); + } + + } else if (m_pp1.size () + m_pp2.size () <= m_scanner_thr) { + + // below m_scanner_thr elements use the brute force approach which is faster in that case + + for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) { + for (iterator_type2 j = m_pp2.begin (); j != m_pp2.end (); ++j) { + if (bs_boxes_overlap (bc1 (*i->first), bc2 (*j->first), enl)) { + rec.add (i->first, i->second, j->first, j->second); + } + } + } + + for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) { + rec.finish1 (i->first, i->second); + } + for (iterator_type2 i = m_pp2.begin (); i != m_pp2.end (); ++i) { + rec.finish2 (i->first, i->second); + } + + } else { + + std::set > seen1; + std::set > seen2; + + std::sort (m_pp1.begin (), m_pp1.end (), bottom_side_compare_func1 (bc1)); + std::sort (m_pp2.begin (), m_pp2.end (), bottom_side_compare_func2 (bc2)); + + coord_type y = std::min (bc1 (*m_pp1.front ().first).bottom (), bc2 (*m_pp2.front ().first).bottom ()); + + iterator_type1 current1 = m_pp1.begin (); + iterator_type1 future1 = m_pp1.begin (); + iterator_type2 current2 = m_pp2.begin (); + iterator_type2 future2 = m_pp2.begin (); + + std::auto_ptr progress (0); + if (m_report_progress) { + if (m_progress_desc.empty ()) { + progress.reset (new tl::RelativeProgress (tl::to_string (tr ("Processing")), m_pp1.size () + m_pp2.size (), 1000)); + } else { + progress.reset (new tl::RelativeProgress (m_progress_desc, m_pp1.size () + m_pp2.size (), 1000)); + } + } + + while (future1 != m_pp1.end () || future2 != m_pp2.end ()) { + + iterator_type1 cc1 = current1; + iterator_type2 cc2 = current2; + current1 = std::partition (current1, future1, below_func1 (bc1, y + 1 - enl)); + current2 = std::partition (current2, future2, below_func2 (bc2, y + 1 - enl)); + + while (cc1 != current1) { + rec.finish1 (cc1->first, cc1->second); + typename std::set >::iterator s; + s = seen1.lower_bound (std::make_pair (cc1->first, (const Obj2 *)0)); + while (s != seen1.end () && s->first == cc1->first) { + seen1.erase (s++); + } + ++cc1; + } + + while (cc2 != current2) { + rec.finish2 (cc2->first, cc2->second); + typename std::set >::iterator s; + s = seen2.lower_bound (std::make_pair (cc2->first, (const Obj1 *)0)); + while (s != seen2.end () && s->first == cc2->first) { + seen2.erase (s++); + } + ++cc2; + } + + // add at least the required items per band + size_t min_band_size = size_t ((future1 - current1) * m_fill_factor) + size_t ((future2 - current2) * m_fill_factor); + coord_type yy = y; + do { + if (future1 != m_pp1.end () && future2 != m_pp2.end ()) { + yy = std::min (bc1 (*future1->first).bottom (), bc2 (*future2->first).bottom ()); + } else if (future1 != m_pp1.end ()) { + yy = bc1 (*future1->first).bottom (); + } else { + yy = bc2 (*future2->first).bottom (); + } + while (future1 != m_pp1.end () && bc1 (*future1->first).bottom () == yy) { + ++future1; + } + while (future2 != m_pp2.end () && bc2 (*future2->first).bottom () == yy) { + ++future2; + } + } while ((future1 != m_pp1.end () || future2 != m_pp2.end ()) && size_t (future1 - current1) + size_t (future2 - current2) < min_band_size); + + if (current1 != future1 && current2 != future2) { + + std::sort (current1, future1, left_side_compare_func1 (bc1)); + std::sort (current2, future2, left_side_compare_func2 (bc2)); + + iterator_type1 c1 = current1; + iterator_type1 f1 = current1; + iterator_type2 c2 = current2; + iterator_type2 f2 = current2; + + coord_type x = std::min (bc1 (*c1->first).left (), bc2 (*c2->first).left ()); + + while (f1 != future1 || f2 != future2) { + + c1 = std::partition (c1, f1, left_func1 (bc1, x + 1 - enl)); + c2 = std::partition (c2, f2, left_func2 (bc2, x + 1 - enl)); + + // add at least the required items per band + size_t min_box_size = size_t ((f1 - c1) * m_fill_factor) + size_t ((f2 - c2) * m_fill_factor); + coord_type xx = x; + do { + if (f1 != future1 && f2 != future2) { + xx = std::min (bc1 (*f1->first).left (), bc2 (*f2->first).left ()); + } else if (f1 != future1) { + xx = bc1 (*f1->first).left (); + } else if (f2 != future2) { + xx = bc2 (*f2->first).left (); + } + while (f1 != future1 && bc1 (*f1->first).left () == xx) { + ++f1; + } + while (f2 != future2 && bc2 (*f2->first).left () == xx) { + ++f2; + } + } while ((f1 != future1 || f2 != future2) && size_t (f1 - c1) + size_t (f2 - c2) < min_box_size); + + if (c1 != f1 && c2 != f2) { + for (iterator_type1 i = c1; i != f1; ++i) { + for (iterator_type2 j = c2; j < f2; ++j) { + if (bs_boxes_overlap (bc1 (*i->first), bc2 (*j->first), enl)) { + if (seen1.insert (std::make_pair (i->first, j->first)).second) { + seen2.insert (std::make_pair (j->first, i->first)); + rec.add (i->first, i->second, j->first, j->second); + } + } + } + } + } + + x = xx; + + if (m_report_progress) { + progress->set (std::min (f1 - m_pp1.begin (), f2 - m_pp2.begin ())); + } + + } + + } + + y = yy; + + } + + while (current1 != m_pp1.end ()) { + rec.finish1 (current1->first, current1->second); + ++current1; + } + while (current2 != m_pp2.end ()) { + rec.finish2 (current2->first, current2->second); + ++current2; + } + + } + + } + +private: + container_type1 m_pp1; + container_type2 m_pp2; + double m_fill_factor; + size_t m_scanner_thr; + bool m_report_progress; + std::string m_progress_desc; +}; + /** * @brief A cluster template that stores properties * diff --git a/src/db/unit_tests/dbBoxScanner.cc b/src/db/unit_tests/dbBoxScanner.cc index 9ecc06d6b..211b5a4fa 100644 --- a/src/db/unit_tests/dbBoxScanner.cc +++ b/src/db/unit_tests/dbBoxScanner.cc @@ -56,6 +56,37 @@ struct BoxScannerTestRecorder2 std::set > interactions; }; +struct BoxScannerTestRecorderTwo +{ + void finish1 (const db::Box * /*box*/, size_t p) { + str += "<" + tl::to_string (p) + ">"; + } + + void finish2 (const db::SimplePolygon * /*poly*/, int p) { + str += "<" + tl::to_string (p) + ">"; + } + + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) + { + str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; + } + + std::string str; +}; + +struct BoxScannerTestRecorder2Two +{ + void finish1 (const db::Box *, size_t) { } + void finish2 (const db::SimplePolygon *, int) { } + + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) + { + interactions.insert (std::make_pair (p1, p2)); + } + + std::set > interactions; +}; + TEST(1) { db::box_scanner bs; @@ -787,3 +818,219 @@ TEST(100) } + +TEST(two_1) +{ + db::box_scanner2 bs; + + std::vector bb; + std::vector bb2; + bb.push_back (db::Box (0, 210, 200, 310)); + bb.push_back (db::Box (10, 220, 210, 320)); + bb.push_back (db::Box (0, 0, 100, 100)); + bb.push_back (db::Box (50, 50, 150, 150)); + bb.push_back (db::Box (10, 10, 110, 110)); + bb.push_back (db::Box (100, 10, 200, 110)); + + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bb2.push_back (db::SimplePolygon (*b)); + } + + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert1 (&*b, b - bb.begin ()); + } + for (std::vector::const_iterator b = bb2.begin (); b != bb2.end (); ++b) { + bs.insert2 (&*b, int (b - bb2.begin ()) + 10); + } + + BoxScannerTestRecorderTwo tr; + bs.set_fill_factor (0.0); + db::box_convert bc1; + db::box_convert bc2; + bs.set_scanner_threshold (0); + bs.process (tr, 1, bc1, bc2); + EXPECT_EQ (tr.str, "(2-12)(2-14)(4-12)(4-14)(2-15)(4-15)(5-12)(5-14)(5-15)(2-13)(4-13)(3-12)(3-14)(3-13)(3-15)(5-13)(0-10)<2><5><4><3><12><15><14><13>(0-11)(1-10)(1-11)<0><1><10><11>"); +} + +TEST(two_1a) +{ + db::box_scanner2 bs; + + std::vector bb; + bb.push_back (db::Box (0, 210, 200, 310)); + //bb.push_back (db::Box (10, 220, 210, 320)); + //bb.push_back (db::Box (0, 0, 100, 100)); + bb.push_back (db::Box (50, 50, 150, 150)); + bb.push_back (db::Box (10, 10, 110, 110)); + //bb.push_back (db::Box (100, 10, 200, 110)); + + std::vector bb2; + //bb2.push_back (db::SimplePolygon (db::Box (0, 210, 200, 310))); + bb2.push_back (db::SimplePolygon (db::Box (10, 220, 210, 320))); + bb2.push_back (db::SimplePolygon (db::Box (0, 0, 100, 100))); + //bb2.push_back (db::SimplePolygon (db::Box (50, 50, 150, 150))); + //bb2.push_back (db::SimplePolygon (db::Box (10, 10, 110, 110))); + bb2.push_back (db::SimplePolygon (db::Box (100, 10, 200, 110))); + + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert1 (&*b, b - bb.begin ()); + } + for (std::vector::const_iterator b = bb2.begin (); b != bb2.end (); ++b) { + bs.insert2 (&*b, int (b - bb2.begin ()) + 10); + } + + BoxScannerTestRecorderTwo tr; + bs.set_fill_factor (0.0); + db::box_convert bc1; + db::box_convert bc2; + bs.set_scanner_threshold (0); + bs.process (tr, 1, bc1, bc2); + EXPECT_EQ (tr.str, "(2-11)(2-12)(1-11)(1-12)<1><2><11><12>(0-10)<0><10>"); +} + +TEST(two_1b) +{ + db::box_scanner2 bs; + + std::vector bb; + //bb.push_back (db::Box (0, 210, 200, 310)); + bb.push_back (db::Box (10, 220, 210, 320)); + bb.push_back (db::Box (0, 0, 100, 100)); + //bb.push_back (db::Box (50, 50, 150, 150)); + //bb.push_back (db::Box (10, 10, 110, 110)); + bb.push_back (db::Box (100, 10, 200, 110)); + + std::vector bb2; + bb2.push_back (db::SimplePolygon (db::Box (0, 210, 200, 310))); + //bb2.push_back (db::SimplePolygon (db::Box (10, 220, 210, 320))); + //bb2.push_back (db::SimplePolygon (db::Box (0, 0, 100, 100))); + bb2.push_back (db::SimplePolygon (db::Box (50, 50, 150, 150))); + bb2.push_back (db::SimplePolygon (db::Box (10, 10, 110, 110))); + //bb2.push_back (db::SimplePolygon (db::Box (100, 10, 200, 110))); + + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert1 (&*b, b - bb.begin ()); + } + for (std::vector::const_iterator b = bb2.begin (); b != bb2.end (); ++b) { + bs.insert2 (&*b, int (b - bb2.begin ()) + 10); + } + + BoxScannerTestRecorderTwo tr; + bs.set_fill_factor (0.0); + db::box_convert bc1; + db::box_convert bc2; + bs.set_scanner_threshold (0); + bs.process (tr, 1, bc1, bc2); + EXPECT_EQ (tr.str, "(1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>"); +} + +void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) +{ + std::vector bb; + for (size_t i = 0; i < n; ++i) { + db::Coord x = rand () % spread; + db::Coord y = rand () % spread; + bb.push_back (db::Box (x, y, x + 100, y + 100)); + // std::cout << "Box 1" << bb.back ().to_string () << std::endl; + } + + std::vector bb2; + for (size_t i = 0; i < n; ++i) { + db::Coord x = rand () % spread; + db::Coord y = rand () % spread; + bb2.push_back (db::SimplePolygon (db::Box (x, y, x + 100, y + 100))); + // std::cout << "Polygon 2" << bb2.back ().to_string () << std::endl; + } + + db::box_scanner2 bs; + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert1 (&*b, b - bb.begin ()); + } + for (std::vector::const_iterator b2 = bb2.begin (); b2 != bb2.end (); ++b2) { + bs.insert2 (&*b2, int (b2 - bb2.begin ())); + } + + BoxScannerTestRecorder2Two tr; + bs.set_fill_factor (ff); + db::box_convert bc1; + db::box_convert bc2; + { + tl::SelfTimer timer ("box-scanner"); + bs.set_scanner_threshold (0); + bs.process (tr, touch ? 1 : 0, bc1, bc2); + } + + std::set > interactions; + { + tl::SelfTimer timer ("brute-force"); + for (size_t i = 0; i < bb.size (); ++i) { + for (size_t j = 0; j < bb2.size (); ++j) { + if ((touch && bb[i].touches (bb2[j].box ())) || (!touch && bb[i].overlaps (bb2[j].box ()))) { + interactions.insert (std::make_pair (i, int (j))); + } + } + } + } + + if (interactions != tr.interactions) { + tl::info << "Interactions 1-2 in 'brute force' but not in 'box-scanner':"; + for (std::set >::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { + if (tr.interactions.find (*i) == tr.interactions.end ()) { + tl::info << " " << i->first << "-" << i->second; + } + } + tl::info << "Interactions 1-2 in 'box-scanner' but not in 'brute force':"; + for (std::set >::const_iterator i = tr.interactions.begin (); i != tr.interactions.end (); ++i) { + if (interactions.find (*i) == interactions.end ()) { + tl::info << " " << i->first << "-" << i->second; + } + } + } + EXPECT_EQ (interactions == tr.interactions, true); + +} + +TEST(two_2a) +{ + run_test2_two(_this, 10, 0.0, 1000); +} + +TEST(two_2b) +{ + run_test2_two(_this, 10, 0.0, 100); +} + +TEST(two_2c) +{ + run_test2_two(_this, 10, 0.0, 10); +} + +TEST(two_2d) +{ + run_test2_two(_this, 1000, 0.0, 1000); +} + +TEST(two_2e) +{ + run_test2_two(_this, 1000, 2, 1000); +} + +TEST(two_2f) +{ + run_test2_two(_this, 1000, 2, 1000, false); +} + +TEST(two_2g) +{ + run_test2_two(_this, 1000, 2, 500); +} + +TEST(two_2h) +{ + run_test2_two(_this, 1000, 2, 100); +} + +TEST(two_2i) +{ + run_test2_two(_this, 10000, 2, 10000); +} From 3214200e17b6fe94fd32aff4b824bbbf0e32c653 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Sep 2018 02:45:56 +0200 Subject: [PATCH 003/335] WIP: code factory --- src/db/unit_tests/dbPolygonTools.cc | 1 + .../tools/netx/db_plugin/dbNetExtractor.cc | 613 +++++++++++++++++- .../tools/netx/db_plugin/dbNetExtractor.h | 55 +- .../netx/db_plugin/gsiDeclDbNetExtractor.cc | 31 +- .../tools/netx/unit_tests/unit_tests.pro | 2 +- 5 files changed, 695 insertions(+), 7 deletions(-) diff --git a/src/db/unit_tests/dbPolygonTools.cc b/src/db/unit_tests/dbPolygonTools.cc index 06224c533..79880ccd0 100644 --- a/src/db/unit_tests/dbPolygonTools.cc +++ b/src/db/unit_tests/dbPolygonTools.cc @@ -2241,3 +2241,4 @@ TEST(403) EXPECT_EQ (right_of.size (), size_t (0)); } } + diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc index 584a7583c..3bdb4b23d 100644 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc @@ -22,23 +22,628 @@ #include "dbNetExtractor.h" +#include "dbLayoutUtils.h" +#include "dbCellMapping.h" +#include "dbPolygonTools.h" +#include "dbBoxScanner.h" +#include "dbRecursiveShapeIterator.h" +#include "dbBoxConvert.h" #include "tlLog.h" namespace db { +// ------------------------------------------------------------------------------------------ + +class LocalOperation +{ +public: + LocalOperation () { } + + virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const = 0; +}; + +// ------------------------------------------------------------------------------------------ + +class LocalProcessorCellContext; + +struct LocalProcessorCellDrop +{ + LocalProcessorCellDrop (db::LocalProcessorCellContext *_parent_context, db::Cell *_parent, const db::ICplxTrans &_cell_inst) + : parent_context (_parent_context), parent (_parent), cell_inst (_cell_inst) + { + // .. nothing yet .. + } + + db::LocalProcessorCellContext *parent_context; + db::Cell *parent; + db::ICplxTrans cell_inst; +}; + +// --------------------------------------------------------------------------------------------- + +class LocalProcessorCellContext +{ +public: + typedef std::pair parent_inst_type; + + LocalProcessorCellContext () + { + // .. nothing yet .. + } + + void add (db::LocalProcessorCellContext *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst) + { + m_drops.push_back (LocalProcessorCellDrop (parent_context, parent, cell_inst)); + } + + std::set &propagated () + { + return m_propagated; + } + + void propagate (const std::set &res) + { + if (res.empty ()) { + return; + } + + for (std::vector::const_iterator d = m_drops.begin (); d != m_drops.end (); ++d) { + + tl_assert (d->parent_context != 0); + tl_assert (d->parent != 0); + + db::Layout *layout = d->parent->layout (); + + for (std::set::const_iterator r = res.begin (); r != res.end (); ++r) { + db::Polygon poly = r->obj ().transformed (d->cell_inst * db::ICplxTrans (r->trans ())); + d->parent_context->propagated ().insert (db::PolygonRef (poly, layout->shape_repository ())); + } + + } + + } + +private: + std::set m_propagated; + std::vector m_drops; +}; + +// --------------------------------------------------------------------------------------------- + +class LocalProcessor; + +class LocalProcessorCellContexts +{ +public: + typedef std::pair, std::set > key_type; + + LocalProcessorCellContexts (); + + db::LocalProcessorCellContext *find_context (const key_type &intruders); + db::LocalProcessorCellContext *create (const key_type &intruders); + void compute_results (db::Cell *cell, LocalProcessor *proc); + +private: + std::map m_contexts; +}; + +// --------------------------------------------------------------------------------------------- + +class LocalProcessor +{ +public: + LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer); + void run (); + +private: + friend class LocalProcessorCellContexts; + typedef std::map contexts_per_cell_type; + + db::Layout *mp_layout; + db::Cell *mp_top; + unsigned int m_scope_layer, m_intruder_layer, m_output_layer; + contexts_per_cell_type m_contexts_per_cell; + LocalOperation *mp_op; + + void compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders); + void compute_results (); + void push_results (db::Cell *cell, const std::set &result); + void compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const; +}; + +// --------------------------------------------------------------------------------------------- + +LocalProcessorCellContexts::LocalProcessorCellContexts () +{ + // .. nothing yet .. +} + +db::LocalProcessorCellContext * +LocalProcessorCellContexts::find_context (const key_type &intruders) +{ + std::map::iterator c = m_contexts.find (intruders); + return c != m_contexts.end () ? &c->second : 0; +} + +db::LocalProcessorCellContext * +LocalProcessorCellContexts::create (const key_type &intruders) +{ + return &m_contexts[intruders]; +} + +void +LocalProcessorCellContexts::compute_results (db::Cell *cell, LocalProcessor *proc) +{ + bool first = true; + std::set common; + + for (std::map::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { + + if (first) { + + common = c->second.propagated (); + proc->compute_local_cell (cell, c->first, common); + first = false; + + } else { + + std::set res = c->second.propagated (); + proc->compute_local_cell (cell, c->first, res); + + if (common.empty ()) { + + c->second.propagate (res); + + } else if (res != common) { + + std::set lost; + std::set_difference (common.begin (), common.end (), res.begin (), res.end (), std::inserter (lost, lost.end ())); + + if (! lost.empty ()) { + + std::set new_common; + std::set_intersection (common.begin (), common.end (), res.begin (), res.end (), std::inserter (new_common, new_common.end ())); + common.swap (new_common); + + for (std::map::iterator cc = m_contexts.begin (); cc != c; ++cc) { + cc->second.propagate (lost); + } + + } + + std::set gained; + std::set_difference (res.begin (), res.end (), common.begin (), common.end (), std::inserter (gained, gained.end ())); + c->second.propagate (gained); + + } + + } + + } + + proc->push_results (cell, common); +} + +// --------------------------------------------------------------------------------------------- + +namespace +{ + +struct InteractionRegistrationShape2Shape + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationShape2Shape (std::map > *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const db::PolygonRef *ref1, int, const db::PolygonRef *ref2, int) + { + (*mp_result) [*ref1].push_back (*ref2); + } + +private: + std::map > *mp_result; +}; + +struct InteractionRegistrationShape2Inst + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationShape2Inst (db::Layout *layout, unsigned int intruder_layer, std::map > *result) + : mp_layout (layout), m_intruder_layer (intruder_layer), m_inst_bc (*layout, intruder_layer), mp_result (result) + { + // nothing yet .. + } + + void add (const db::PolygonRef *ref, int, const db::CellInstArray *inst, int) + { + const db::Cell &intruder_cell = mp_layout->cell (inst->object ().cell_index ()); + db::Box region = ref->box () & m_inst_bc (*inst); + if (! region.empty ()) { + + // @@@ TODO: should be lighter, cache, handle arrays .. + db::RecursiveShapeIterator si (*mp_layout, intruder_cell, m_intruder_layer, region); + si.shape_flags (db::ShapeIterator::PolygonRef); + while (! si.at_end ()) { + + // @@@ should be easier to transform references + const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); + db::Polygon poly = ref2->obj ().transformed (si.trans () * db::ICplxTrans (ref->trans ())); + (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_layout->shape_repository())); + + ++si; + + } + + } + } + +private: + db::Layout *mp_layout; + unsigned int m_intruder_layer; + db::box_convert m_inst_bc; + std::map > *mp_result; +}; + +struct InteractionRegistrationInst2Inst + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationInst2Inst (std::map, std::set > > *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const db::CellInstArray *inst1, int, const db::CellInstArray *inst2, int) + { + (*mp_result) [inst1].first.insert (inst2); + } + +private: + std::map, std::set > > *mp_result; +}; + +struct InteractionRegistrationInst2Shape + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationInst2Shape (std::map, std::set > > *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const db::CellInstArray *inst, int, const db::PolygonRef *ref, int) + { + (*mp_result) [inst].second.insert (*ref); + } + +private: + std::map, std::set > > *mp_result; +}; + +} + +// --------------------------------------------------------------------------------------------- + +LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer) + : mp_layout (layout), mp_top (top), m_scope_layer (scope_layer), m_intruder_layer (intruder_layer), m_output_layer (output_layer), mp_op (op) +{ + // .. nothing yet .. +} + +void LocalProcessor::run () +{ + try { + + mp_layout->update (); + mp_layout->start_changes (); + + std::pair, std::set > intruders; + compute_contexts (0, 0, mp_top, db::ICplxTrans (), intruders); + compute_results (); + + mp_layout->end_changes (); + + } catch (...) { + mp_layout->end_changes (); + throw; + } +} + +void LocalProcessor::push_results (db::Cell *cell, const std::set &result) +{ + if (! result.empty ()) { + cell->shapes (m_output_layer).insert (result.begin (), result.end ()); + } +} + +void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders) +{ + db::LocalProcessorCellContexts &contexts = m_contexts_per_cell [cell]; + + db::LocalProcessorCellContext *context = contexts.find_context (intruders); + if (context) { + context->add (parent_context, parent, cell_inst); + return; + } + + context = contexts.create (intruders); + context->add (parent_context, parent, cell_inst); + + const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); + + db::box_convert inst_bcs (*mp_layout, m_scope_layer); + db::box_convert inst_bci (*mp_layout, m_intruder_layer); + db::box_convert inst_bcii (*mp_layout, m_intruder_layer); + + if (! cell->begin ().at_end ()) { + + typedef std::pair, std::set > interaction_value_type; + + std::map interactions; + + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ())); + } + + { + db::box_scanner2 scanner; + InteractionRegistrationInst2Inst rec (&interactions); + + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + scanner.insert1 (&i->cell_inst (), 0); + scanner.insert2 (&i->cell_inst (), 0); + } + + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + + scanner.process (rec, 0, inst_bcs, inst_bci); + } + + { + db::box_scanner2 scanner; + InteractionRegistrationInst2Shape rec (&interactions); + + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + scanner.insert1 (&i->cell_inst (), 0); + } + + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + for (db::Shapes::shape_iterator i = shapes_intruders.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + scanner.process (rec, 0, inst_bcs, db::box_convert ()); + } + + for (std::map, std::set > >::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { + + std::pair, std::set > intruders_below; + intruders_below.second = i->second.second; + + db::Cell &child_cell = mp_layout->cell (i->first->object ().cell_index ()); + + for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) { + + db::ICplxTrans tn = i->first->complex_trans (*n); + db::ICplxTrans tni = tn.inverted (); + db::Box nbox = tn * child_cell.bbox (m_intruder_layer); + + if (! nbox.empty ()) { + + intruders_below.first.clear (); + + // @@@ TODO: in some cases, it may be possible to optimize this for arrays + + for (std::set::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) { + for (db::CellInstArray::iterator k = (*j)->begin_touching (nbox.enlarged (db::Vector (-1, -1)), inst_bcii); ! k.at_end (); ++k) { + intruders_below.first.insert (db::CellInstArray (db::CellInst ((*j)->object ().cell_index ()), tni * (*j)->complex_trans (*k))); + } + } + + compute_contexts (context, cell, &child_cell, tn, intruders_below); + + } + + } + + } + + } +} + +void +LocalProcessor::compute_results () +{ + for (db::Layout::bottom_up_const_iterator bu = mp_layout->begin_bottom_up (); bu != mp_layout->end_bottom_up (); ++bu) { + + contexts_per_cell_type::iterator cpc = m_contexts_per_cell.find (&mp_layout->cell (*bu)); + if (cpc != m_contexts_per_cell.end ()) { + cpc->second.compute_results (cpc->first, this); + m_contexts_per_cell.erase (cpc); + } + + } + +} + +void +LocalProcessor::compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const +{ + const db::Shapes &shapes_scope = cell->shapes (m_scope_layer); + const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); + + // local shapes vs. child cell + + std::map > interactions; + db::box_convert inst_bci (*mp_layout, m_intruder_layer); + + for (db::Shapes::shape_iterator i = shapes_scope.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { + interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); + } + + if (! shapes_scope.empty () && ! (shapes_intruders.empty () && intruders.second.empty ())) { + + db::box_scanner2 scanner; + InteractionRegistrationShape2Shape rec (&interactions); + + for (db::Shapes::shape_iterator i = shapes_scope.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { + scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + for (db::Shapes::shape_iterator i = shapes_intruders.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + scanner.process (rec, 0, db::box_convert (), db::box_convert ()); + + } + + if (! shapes_scope.empty () && ! (cell->begin ().at_end () && intruders.first.empty ())) { + + db::box_scanner2 scanner; + InteractionRegistrationShape2Inst rec (mp_layout, m_intruder_layer, &interactions); + + for (db::Shapes::shape_iterator i = shapes_scope.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { + scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + scanner.insert2 (&i->cell_inst (), 0); + } + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + + scanner.process (rec, 0, db::box_convert (), inst_bci); + + } + + mp_op->compute_local (mp_layout, interactions, result); +} + +// ------------------------------------------------------------------------------------------ + NetExtractor::NetExtractor() + : mp_orig_layout (0), mp_layout (0) { // @@@ } -void NetExtractor::dummy() +NetExtractor::~NetExtractor () { - // @@@ - tl::log << "@@@ this is net extractor!"; - // @@@ + delete mp_layout; + mp_layout = 0; +} + +void +NetExtractor::open (const db::Layout &orig_layout, cell_index_type orig_top_cell) +{ + delete mp_layout; + mp_orig_layout = &orig_layout; + + mp_layout = new db::Layout (); + mp_layout->dbu (orig_layout.dbu ()); + db::cell_index_type top = mp_layout->add_cell (orig_layout.cell_name (orig_top_cell)); + + // copy hierarchy + m_cm.clear (); + m_cm.create_from_names_full (*mp_layout, top, orig_layout, orig_top_cell); +} + +static double area_ratio (const db::Polygon &poly) +{ + return double (poly.box ().area ()) / double (poly.area ()); +} + +static void split_polygon_into (const db::Polygon &poly, db::Shapes &dest, size_t max_points, double max_area_ratio) +{ + size_t npoints = 0; + for (unsigned int c = 0; c < poly.holes () + 1; ++c) { + npoints += poly.contour (c).size (); + } + + if (npoints > max_points || area_ratio (poly) > max_area_ratio) { + + std::vector split_polygons; + db::split_polygon (poly, split_polygons); + for (std::vector ::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) { + split_polygon_into (*sp, dest, max_points, max_area_ratio); + } + + } else { + + dest.insert (db::PolygonRef (poly, dest.layout ()->shape_repository ())); + + } +} + +NetLayer +NetExtractor::load (unsigned int layer_index) +{ + const double max_area_ratio = 3.0; + const size_t max_points = 16; + + NetLayer lt (mp_layout->insert_layer ()); + mp_layout->set_properties (lt.layer_index(), mp_orig_layout->get_properties (layer_index)); + + for (db::Layout::const_iterator c = mp_orig_layout->begin (); c != mp_layout->end (); ++c) { + + if (m_cm.has_mapping (c->cell_index ())) { + + db::cell_index_type ct = m_cm.cell_mapping (c->cell_index ()); + + db::Shapes &dest_shapes = mp_layout->cell (ct).shapes (lt.layer_index()); + const db::Shapes &orig_shapes = c->shapes (layer_index); + for (db::Shapes::shape_iterator s = orig_shapes.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); ! s.at_end (); ++s) { + + // @@@ TODO: cache splitting and path to polygon conversion + db::Polygon poly; + s->polygon (poly); + + split_polygon_into (poly, dest_shapes, max_points, max_area_ratio); + + } + + } + + } + + return lt; +} + +NetLayer +NetExtractor::bool_and (NetLayer a, NetLayer b) +{ + return NetLayer (mp_layout->insert_layer ()); // @@@ +} + +NetLayer +NetExtractor::bool_not (NetLayer a, NetLayer b) +{ + return NetLayer (mp_layout->insert_layer ()); // @@@ +} + +db::Layout * +NetExtractor::layout_copy () const +{ + tl_assert (mp_layout != 0); + return new db::Layout (*mp_layout); } } diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.h b/src/plugins/tools/netx/db_plugin/dbNetExtractor.h index 64f107b1e..2f478e93f 100644 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.h +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.h @@ -26,10 +26,31 @@ #define HDR_dbNetExtractor #include "dbPluginCommon.h" +#include "dbLayout.h" +#include "dbCellMapping.h" +#include "tlTypeTraits.h" namespace db { +class NetLayer +{ +public: + NetLayer (unsigned int index) + : m_layer_index (index) + { + // .. nothing yet .. + } + + unsigned int layer_index () const + { + return m_layer_index; + } + +private: + unsigned int m_layer_index; +}; + /** * @brief The net extractor * @@ -43,11 +64,43 @@ public: */ NetExtractor (); + ~NetExtractor (); + // @@@ - void dummy (); + void open (const db::Layout &orig_layout, db::cell_index_type orig_top_cell); + NetLayer load (unsigned int layer_index); + NetLayer bool_and (NetLayer a, NetLayer b); + NetLayer bool_not (NetLayer a, NetLayer b); + db::Layout *layout_copy () const; private: + // no copying + NetExtractor (const db::NetExtractor &); + NetExtractor &operator= (const db::NetExtractor &); + // @@@ + const db::Layout *mp_orig_layout; // @@@ should be a smart pointer + db::Layout *mp_layout; + db::CellMapping m_cm; +}; + +} + +namespace tl +{ + +template <> +struct type_traits : public tl::type_traits +{ + // mark "NetLayer" as not having a default ctor + typedef tl::false_tag has_default_constructor; +}; + +template <> +struct type_traits : public tl::type_traits +{ + // mark "NetExtractor" as not copyable + typedef tl::false_tag has_copy_constructor; }; } diff --git a/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc index ef2b53845..a3a549902 100644 --- a/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc +++ b/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc @@ -27,8 +27,37 @@ namespace gsi { +gsi::Class decl_NetLayer ("db", "NetLayer", + gsi::method ("layer_index", &db::NetLayer::layer_index, + "@@@" + ), + "@brief The net extractor\n" + "\n" + "This class has been introduced in version 0.26." +); + +void open2 (db::NetExtractor *ex, const db::Layout *orig_layout, const db::Cell *cell) +{ + ex->open (*orig_layout, cell->cell_index ()); +} + gsi::Class decl_NetNetExtractor ("db", "NetExtractor", - gsi::method ("dummy", &db::NetExtractor::dummy, + gsi::method ("open", &db::NetExtractor::open, gsi::arg ("orig_layout"), gsi::arg ("orig_top_cell_index"), + "@@@" + ) + + gsi::method_ext ("open", &open2, gsi::arg ("orig_layout"), gsi::arg ("orig_top_cell"), + "@@@" + ) + + gsi::method ("load", &db::NetExtractor::load, gsi::arg ("layer_index"), + "@@@" + ) + + gsi::method ("bool_and", &db::NetExtractor::bool_and, gsi::arg ("a"), gsi::arg ("b"), + "@@@" + ) + + gsi::method ("bool_not", &db::NetExtractor::bool_not, gsi::arg ("a"), gsi::arg ("b"), + "@@@" + ) + + gsi::factory ("layout_copy", &db::NetExtractor::layout_copy, "@@@" ), "@brief The net extractor\n" diff --git a/src/plugins/tools/netx/unit_tests/unit_tests.pro b/src/plugins/tools/netx/unit_tests/unit_tests.pro index ac1f944ea..5f51b3fff 100644 --- a/src/plugins/tools/netx/unit_tests/unit_tests.pro +++ b/src/plugins/tools/netx/unit_tests/unit_tests.pro @@ -6,7 +6,7 @@ TARGET = net_tracer_tests include($$PWD/../../../../lib_ut.pri) SOURCES = \ - dbNetExtractor.cc \ + dbNetExtractorTests.cc \ INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common From a551300e0d7ebece675b99b7012b85e87b7db207 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Sep 2018 22:32:36 +0200 Subject: [PATCH 004/335] WIP: Some refactoring (hier processing in extra file), unit tests, bug fixes --- src/db/db/dbTestSupport.cc | 15 +- src/db/db/dbTestSupport.h | 1 + .../tools/netx/db_plugin/dbHierProcessor.cc | 556 ++++++++++++++++++ .../tools/netx/db_plugin/dbHierProcessor.h | 150 +++++ .../tools/netx/db_plugin/dbNetExtractor.cc | 504 ---------------- .../tools/netx/db_plugin/db_plugin.pro | 2 + src/plugins/tools/netx/testdata/hlp1.oas | Bin 0 -> 583 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 112 ++++ .../tools/netx/unit_tests/unit_tests.pro | 1 + 9 files changed, 832 insertions(+), 509 deletions(-) create mode 100644 src/plugins/tools/netx/db_plugin/dbHierProcessor.cc create mode 100644 src/plugins/tools/netx/db_plugin/dbHierProcessor.h create mode 100644 src/plugins/tools/netx/testdata/hlp1.oas create mode 100644 src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index 1698385f6..f8d7516e0 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -50,6 +50,9 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: hash = (hash << 4) ^ (hash >> 4) ^ ((unsigned int) *cp); } + const db::Layout *subject = 0; + db::Layout layout2; + std::string tmp_file; db::SaveLayoutOptions options; @@ -67,10 +70,7 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: writer.write (const_cast (layout), stream); } - const db::Layout *subject = 0; - db::Layout layout2; - - if (norm != NoNormalization) { + if (norm == WriteGDS2 || norm == WriteOAS) { // read all layers from the original layout, so the layer table is the same for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { @@ -121,7 +121,12 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: db::Reader reader (stream); reader.read (layout_au, options); - equal = db::compare_layouts (*subject, layout_au, (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) | db::layout_diff::f_flatten_array_insts /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/, tolerance, 100 /*max diff lines*/); + equal = db::compare_layouts (*subject, layout_au, + (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) + | (norm == AsPolygons ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0) + | db::layout_diff::f_flatten_array_insts + /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ + , tolerance, 100 /*max diff lines*/); if (equal && n > 0) { tl::info << tl::sprintf ("Found match on golden reference variant %s", fn); } diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index a79fcc469..82be9cf34 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -46,6 +46,7 @@ class LayerMap; enum NormalizationMode { NoNormalization, // no normalization - take the test subject as it is + AsPolygons, // paths and boxes are treated as polygons WriteGDS2, // normalize subject by writing to GDS2 and reading back WriteOAS // normalize subject by writing to OASIS and reading back }; diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc new file mode 100644 index 000000000..c84e9fae0 --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -0,0 +1,556 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbHierProcessor.h" +#include "dbBoxScanner.h" +#include "dbRecursiveShapeIterator.h" +#include "dbBoxConvert.h" +#include "dbEdgeProcessor.h" +#include "dbPolygonGenerators.h" +#include "tlLog.h" + +namespace db +{ + + +// --------------------------------------------------------------------------------------------- +// BoolAndOrNotLocalOperation implementation + +namespace { + +class PolygonRefGenerator + : public PolygonSink +{ +public: + /** + * @brief Constructor specifying an external vector for storing the polygons + */ + PolygonRefGenerator (db::Layout *layout, std::set &polyrefs) + : PolygonSink (), mp_layout (layout), mp_polyrefs (&polyrefs) + { } + + /** + * @brief Implementation of the PolygonSink interface + */ + virtual void put (const db::Polygon &polygon) + { + mp_polyrefs->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); + } + +private: + db::Layout *mp_layout; + std::set *mp_polyrefs; +}; + +} + +BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and) + : m_is_and (is_and) +{ + // .. nothing yet .. +} + +void +BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const +{ + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + + std::set others; + for (std::map >::const_iterator r = interactions.begin (); r != interactions.end (); ++r) { + + // TODO: vector could be set + bool found = false; + for (std::vector::const_iterator i = r->second.begin (); i != r->second.end () && ! found; ++i) { + found = (*i == r->first); + } + if (found) { + // shortcut (and: keep, not: drop) + if (m_is_and) { + result.insert (r->first); + } + } else if (r->second.empty ()) { + // shortcut (not: keep, and: drop) + if (! m_is_and) { + result.insert (r->first); + } + } else { + for (db::PolygonRef::polygon_edge_iterator e = r->first.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + others.insert (r->second.begin (), r->second.end ()); + } + + } + + if (! others.empty () || p1 > 0) { + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); + db::PolygonRefGenerator pr (layout, result); + db::PolygonGenerator pg (pr, true, true); + ep.process (pg, op); + + } +} + +// --------------------------------------------------------------------------------------------- +// LocalProcessorCellContext implementation + +LocalProcessorCellContext::LocalProcessorCellContext () +{ + // .. nothing yet .. +} + +void +LocalProcessorCellContext::add (db::LocalProcessorCellContext *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst) +{ + m_drops.push_back (LocalProcessorCellDrop (parent_context, parent, cell_inst)); +} + +void +LocalProcessorCellContext::propagate (const std::set &res) +{ + if (res.empty ()) { + return; + } + + for (std::vector::const_iterator d = m_drops.begin (); d != m_drops.end (); ++d) { + + tl_assert (d->parent_context != 0); + tl_assert (d->parent != 0); + + db::Layout *layout = d->parent->layout (); + + for (std::set::const_iterator r = res.begin (); r != res.end (); ++r) { + db::Polygon poly = r->obj ().transformed (d->cell_inst * db::ICplxTrans (r->trans ())); + d->parent_context->propagated ().insert (db::PolygonRef (poly, layout->shape_repository ())); + } + + } + +} + +// --------------------------------------------------------------------------------------------- +// LocalProcessorCellContexts implementation + +LocalProcessorCellContexts::LocalProcessorCellContexts () +{ + // .. nothing yet .. +} + +db::LocalProcessorCellContext * +LocalProcessorCellContexts::find_context (const key_type &intruders) +{ + std::map::iterator c = m_contexts.find (intruders); + return c != m_contexts.end () ? &c->second : 0; +} + +db::LocalProcessorCellContext * +LocalProcessorCellContexts::create (const key_type &intruders) +{ + return &m_contexts[intruders]; +} + +void +LocalProcessorCellContexts::compute_results (db::Cell *cell, LocalProcessor *proc) +{ + bool first = true; + std::set common; + + for (std::map::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { + + if (first) { + + common = c->second.propagated (); + proc->compute_local_cell (cell, c->first, common); + first = false; + + } else { + + std::set res = c->second.propagated (); + proc->compute_local_cell (cell, c->first, res); + + if (common.empty ()) { + + c->second.propagate (res); + + } else if (res != common) { + + std::set lost; + std::set_difference (common.begin (), common.end (), res.begin (), res.end (), std::inserter (lost, lost.end ())); + + if (! lost.empty ()) { + + std::set new_common; + std::set_intersection (common.begin (), common.end (), res.begin (), res.end (), std::inserter (new_common, new_common.end ())); + common.swap (new_common); + + for (std::map::iterator cc = m_contexts.begin (); cc != c; ++cc) { + cc->second.propagate (lost); + } + + } + + std::set gained; + std::set_difference (res.begin (), res.end (), common.begin (), common.end (), std::inserter (gained, gained.end ())); + c->second.propagate (gained); + + } + + } + + } + + proc->push_results (cell, common); +} + +// --------------------------------------------------------------------------------------------- +// Helper classes for the LocalProcessor + +namespace +{ + +inline unsigned int polygon_ref_flags () +{ + return 1 << db::ShapeIterator::PolygonRef; +} + +struct InteractionRegistrationShape2Shape + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationShape2Shape (std::map > *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const db::PolygonRef *ref1, int, const db::PolygonRef *ref2, int) + { + (*mp_result) [*ref1].push_back (*ref2); + } + +private: + std::map > *mp_result; +}; + +struct InteractionRegistrationShape2Inst + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationShape2Inst (db::Layout *layout, unsigned int intruder_layer, std::map > *result) + : mp_layout (layout), m_intruder_layer (intruder_layer), m_inst_bc (*layout, intruder_layer), mp_result (result) + { + // nothing yet .. + } + + void add (const db::PolygonRef *ref, int, const db::CellInstArray *inst, int) + { + const db::Cell &intruder_cell = mp_layout->cell (inst->object ().cell_index ()); + db::Box region = ref->box () & m_inst_bc (*inst); + if (! region.empty ()) { + + // @@@ TODO: should be lighter, cache, handle arrays .. + db::RecursiveShapeIterator si (*mp_layout, intruder_cell, m_intruder_layer, region); + si.shape_flags (polygon_ref_flags ()); + while (! si.at_end ()) { + + // @@@ should be easier to transform references + const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); + db::Polygon poly = ref2->obj ().transformed (si.trans () * db::ICplxTrans (ref->trans ())); + (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_layout->shape_repository())); + + ++si; + + } + + } + } + +private: + db::Layout *mp_layout; + unsigned int m_intruder_layer; + db::box_convert m_inst_bc; + std::map > *mp_result; +}; + +struct InteractionRegistrationInst2Inst + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationInst2Inst (std::map, std::set > > *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const db::CellInstArray *inst1, int, const db::CellInstArray *inst2, int) + { + (*mp_result) [inst1].first.insert (inst2); + } + +private: + std::map, std::set > > *mp_result; +}; + +struct InteractionRegistrationInst2Shape + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationInst2Shape (std::map, std::set > > *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const db::CellInstArray *inst, int, const db::PolygonRef *ref, int) + { + (*mp_result) [inst].second.insert (*ref); + } + +private: + std::map, std::set > > *mp_result; +}; + +} + +// --------------------------------------------------------------------------------------------- +// LocalProcessor implementation + +LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer) + : mp_layout (layout), mp_top (top), m_scope_layer (scope_layer), m_intruder_layer (intruder_layer), m_output_layer (output_layer), mp_op (op) +{ + // .. nothing yet .. +} + +void LocalProcessor::run () +{ + try { + + mp_layout->update (); + mp_layout->start_changes (); + + std::pair, std::set > intruders; + compute_contexts (0, 0, mp_top, db::ICplxTrans (), intruders); + compute_results (); + + mp_layout->end_changes (); + + } catch (...) { + mp_layout->end_changes (); + throw; + } +} + +void LocalProcessor::push_results (db::Cell *cell, const std::set &result) +{ + if (! result.empty ()) { + cell->shapes (m_output_layer).insert (result.begin (), result.end ()); + } +} + +void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders) +{ + db::LocalProcessorCellContexts &contexts = m_contexts_per_cell [cell]; + + db::LocalProcessorCellContext *context = contexts.find_context (intruders); + if (context) { + context->add (parent_context, parent, cell_inst); + return; + } + + context = contexts.create (intruders); + context->add (parent_context, parent, cell_inst); + + const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); + + db::box_convert inst_bcs (*mp_layout, m_scope_layer); + db::box_convert inst_bci (*mp_layout, m_intruder_layer); + db::box_convert inst_bcii (*mp_layout, m_intruder_layer); + + if (! cell->begin ().at_end ()) { + + typedef std::pair, std::set > interaction_value_type; + + std::map interactions; + + // @@@ TODO: don't do this in AND more for instances without an intruder + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ())); + } + + { + db::box_scanner2 scanner; + InteractionRegistrationInst2Inst rec (&interactions); + + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + scanner.insert1 (&i->cell_inst (), 0); + scanner.insert2 (&i->cell_inst (), 0); + } + + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + + scanner.process (rec, 0, inst_bcs, inst_bci); + } + + { + db::box_scanner2 scanner; + InteractionRegistrationInst2Shape rec (&interactions); + + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + scanner.insert1 (&i->cell_inst (), 0); + } + + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + for (db::Shapes::shape_iterator i = shapes_intruders.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + scanner.process (rec, 0, inst_bcs, db::box_convert ()); + } + + for (std::map, std::set > >::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { + + std::pair, std::set > intruders_below; + intruders_below.second = i->second.second; + + db::Cell &child_cell = mp_layout->cell (i->first->object ().cell_index ()); + + for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) { + + db::ICplxTrans tn = i->first->complex_trans (*n); + db::ICplxTrans tni = tn.inverted (); + db::Box nbox = tn * child_cell.bbox (m_intruder_layer); + + if (! nbox.empty ()) { + + intruders_below.first.clear (); + + // @@@ TODO: in some cases, it may be possible to optimize this for arrays + + for (std::set::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) { + for (db::CellInstArray::iterator k = (*j)->begin_touching (nbox.enlarged (db::Vector (-1, -1)), inst_bcii); ! k.at_end (); ++k) { + intruders_below.first.insert (db::CellInstArray (db::CellInst ((*j)->object ().cell_index ()), tni * (*j)->complex_trans (*k))); + } + } + + compute_contexts (context, cell, &child_cell, tn, intruders_below); + + } + + } + + } + + } +} + +void +LocalProcessor::compute_results () +{ + for (db::Layout::bottom_up_const_iterator bu = mp_layout->begin_bottom_up (); bu != mp_layout->end_bottom_up (); ++bu) { + + contexts_per_cell_type::iterator cpc = m_contexts_per_cell.find (&mp_layout->cell (*bu)); + if (cpc != m_contexts_per_cell.end ()) { + cpc->second.compute_results (cpc->first, this); + m_contexts_per_cell.erase (cpc); + } + + } + +} + +void +LocalProcessor::compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const +{ + const db::Shapes &shapes_scope = cell->shapes (m_scope_layer); + const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); + + // local shapes vs. child cell + + std::map > interactions; + db::box_convert inst_bci (*mp_layout, m_intruder_layer); + + // @@@ TODO: don't do this in AND mode (we don't need interactions without an intruder) + for (db::Shapes::shape_iterator i = shapes_scope.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); + } + + if (! shapes_scope.empty () && ! (shapes_intruders.empty () && intruders.second.empty ())) { + + db::box_scanner2 scanner; + InteractionRegistrationShape2Shape rec (&interactions); + + for (db::Shapes::shape_iterator i = shapes_scope.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + for (db::Shapes::shape_iterator i = shapes_intruders.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + scanner.process (rec, 0, db::box_convert (), db::box_convert ()); + + } + + if (! shapes_scope.empty () && ! (cell->begin ().at_end () && intruders.first.empty ())) { + + db::box_scanner2 scanner; + InteractionRegistrationShape2Inst rec (mp_layout, m_intruder_layer, &interactions); + + for (db::Shapes::shape_iterator i = shapes_scope.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + scanner.insert2 (&i->cell_inst (), 0); + } + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + + scanner.process (rec, 0, db::box_convert (), inst_bci); + + } + + mp_op->compute_local (mp_layout, interactions, result); +} + +} + diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h new file mode 100644 index 000000000..e8b6707ee --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -0,0 +1,150 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbHierProcessor +#define HDR_dbHierProcessor + +#include "dbLayout.h" +#include "dbPluginCommon.h" + +#include +#include +#include + +namespace db +{ + +class LocalProcessor; +class LocalProcessorCellContext; + +class DB_PLUGIN_PUBLIC LocalOperation +{ +public: + LocalOperation () { } + virtual ~LocalOperation () { } + + virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const = 0; +}; + +class DB_PLUGIN_PUBLIC BoolAndOrNotLocalOperation + : public LocalOperation +{ +public: + BoolAndOrNotLocalOperation (bool is_and); + + virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const; + +private: + bool m_is_and; +}; + +// @@@ TODO: should be hidden (private data?) +struct DB_PLUGIN_PUBLIC LocalProcessorCellDrop +{ + LocalProcessorCellDrop (db::LocalProcessorCellContext *_parent_context, db::Cell *_parent, const db::ICplxTrans &_cell_inst) + : parent_context (_parent_context), parent (_parent), cell_inst (_cell_inst) + { + // .. nothing yet .. + } + + db::LocalProcessorCellContext *parent_context; + db::Cell *parent; + db::ICplxTrans cell_inst; +}; + +// @@@ TODO: should be hidden (private data?) +class DB_PLUGIN_PUBLIC LocalProcessorCellContext +{ +public: + typedef std::pair parent_inst_type; + + LocalProcessorCellContext (); + + void add (db::LocalProcessorCellContext *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst); + void propagate (const std::set &res); + + std::set &propagated () + { + return m_propagated; + } + +private: + std::set m_propagated; + std::vector m_drops; +}; + +class DB_PLUGIN_PUBLIC LocalProcessorCellContexts +{ +public: + typedef std::pair, std::set > key_type; + + LocalProcessorCellContexts (); + + db::LocalProcessorCellContext *find_context (const key_type &intruders); + db::LocalProcessorCellContext *create (const key_type &intruders); + void compute_results (db::Cell *cell, LocalProcessor *proc); + +private: + std::map m_contexts; +}; + +class DB_PLUGIN_PUBLIC LocalProcessor +{ +public: + LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer); + void run (); + +private: + friend class LocalProcessorCellContexts; + typedef std::map contexts_per_cell_type; + + db::Layout *mp_layout; + db::Cell *mp_top; + unsigned int m_scope_layer, m_intruder_layer, m_output_layer; + contexts_per_cell_type m_contexts_per_cell; + LocalOperation *mp_op; + + void compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders); + void compute_results (); + void push_results (db::Cell *cell, const std::set &result); + void compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const; +}; + +} + +namespace tl +{ + +template <> +struct type_traits : public tl::type_traits +{ + // mark "LocalProcessor" as not having a default ctor and no copy ctor + typedef tl::false_tag has_default_constructor; + typedef tl::false_tag has_copy_constructor; +}; + +} + +#endif + diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc index 3bdb4b23d..fe0521e23 100644 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc @@ -33,510 +33,6 @@ namespace db { -// ------------------------------------------------------------------------------------------ - -class LocalOperation -{ -public: - LocalOperation () { } - - virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const = 0; -}; - -// ------------------------------------------------------------------------------------------ - -class LocalProcessorCellContext; - -struct LocalProcessorCellDrop -{ - LocalProcessorCellDrop (db::LocalProcessorCellContext *_parent_context, db::Cell *_parent, const db::ICplxTrans &_cell_inst) - : parent_context (_parent_context), parent (_parent), cell_inst (_cell_inst) - { - // .. nothing yet .. - } - - db::LocalProcessorCellContext *parent_context; - db::Cell *parent; - db::ICplxTrans cell_inst; -}; - -// --------------------------------------------------------------------------------------------- - -class LocalProcessorCellContext -{ -public: - typedef std::pair parent_inst_type; - - LocalProcessorCellContext () - { - // .. nothing yet .. - } - - void add (db::LocalProcessorCellContext *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst) - { - m_drops.push_back (LocalProcessorCellDrop (parent_context, parent, cell_inst)); - } - - std::set &propagated () - { - return m_propagated; - } - - void propagate (const std::set &res) - { - if (res.empty ()) { - return; - } - - for (std::vector::const_iterator d = m_drops.begin (); d != m_drops.end (); ++d) { - - tl_assert (d->parent_context != 0); - tl_assert (d->parent != 0); - - db::Layout *layout = d->parent->layout (); - - for (std::set::const_iterator r = res.begin (); r != res.end (); ++r) { - db::Polygon poly = r->obj ().transformed (d->cell_inst * db::ICplxTrans (r->trans ())); - d->parent_context->propagated ().insert (db::PolygonRef (poly, layout->shape_repository ())); - } - - } - - } - -private: - std::set m_propagated; - std::vector m_drops; -}; - -// --------------------------------------------------------------------------------------------- - -class LocalProcessor; - -class LocalProcessorCellContexts -{ -public: - typedef std::pair, std::set > key_type; - - LocalProcessorCellContexts (); - - db::LocalProcessorCellContext *find_context (const key_type &intruders); - db::LocalProcessorCellContext *create (const key_type &intruders); - void compute_results (db::Cell *cell, LocalProcessor *proc); - -private: - std::map m_contexts; -}; - -// --------------------------------------------------------------------------------------------- - -class LocalProcessor -{ -public: - LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer); - void run (); - -private: - friend class LocalProcessorCellContexts; - typedef std::map contexts_per_cell_type; - - db::Layout *mp_layout; - db::Cell *mp_top; - unsigned int m_scope_layer, m_intruder_layer, m_output_layer; - contexts_per_cell_type m_contexts_per_cell; - LocalOperation *mp_op; - - void compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders); - void compute_results (); - void push_results (db::Cell *cell, const std::set &result); - void compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const; -}; - -// --------------------------------------------------------------------------------------------- - -LocalProcessorCellContexts::LocalProcessorCellContexts () -{ - // .. nothing yet .. -} - -db::LocalProcessorCellContext * -LocalProcessorCellContexts::find_context (const key_type &intruders) -{ - std::map::iterator c = m_contexts.find (intruders); - return c != m_contexts.end () ? &c->second : 0; -} - -db::LocalProcessorCellContext * -LocalProcessorCellContexts::create (const key_type &intruders) -{ - return &m_contexts[intruders]; -} - -void -LocalProcessorCellContexts::compute_results (db::Cell *cell, LocalProcessor *proc) -{ - bool first = true; - std::set common; - - for (std::map::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { - - if (first) { - - common = c->second.propagated (); - proc->compute_local_cell (cell, c->first, common); - first = false; - - } else { - - std::set res = c->second.propagated (); - proc->compute_local_cell (cell, c->first, res); - - if (common.empty ()) { - - c->second.propagate (res); - - } else if (res != common) { - - std::set lost; - std::set_difference (common.begin (), common.end (), res.begin (), res.end (), std::inserter (lost, lost.end ())); - - if (! lost.empty ()) { - - std::set new_common; - std::set_intersection (common.begin (), common.end (), res.begin (), res.end (), std::inserter (new_common, new_common.end ())); - common.swap (new_common); - - for (std::map::iterator cc = m_contexts.begin (); cc != c; ++cc) { - cc->second.propagate (lost); - } - - } - - std::set gained; - std::set_difference (res.begin (), res.end (), common.begin (), common.end (), std::inserter (gained, gained.end ())); - c->second.propagate (gained); - - } - - } - - } - - proc->push_results (cell, common); -} - -// --------------------------------------------------------------------------------------------- - -namespace -{ - -struct InteractionRegistrationShape2Shape - : db::box_scanner_receiver2 -{ -public: - InteractionRegistrationShape2Shape (std::map > *result) - : mp_result (result) - { - // nothing yet .. - } - - void add (const db::PolygonRef *ref1, int, const db::PolygonRef *ref2, int) - { - (*mp_result) [*ref1].push_back (*ref2); - } - -private: - std::map > *mp_result; -}; - -struct InteractionRegistrationShape2Inst - : db::box_scanner_receiver2 -{ -public: - InteractionRegistrationShape2Inst (db::Layout *layout, unsigned int intruder_layer, std::map > *result) - : mp_layout (layout), m_intruder_layer (intruder_layer), m_inst_bc (*layout, intruder_layer), mp_result (result) - { - // nothing yet .. - } - - void add (const db::PolygonRef *ref, int, const db::CellInstArray *inst, int) - { - const db::Cell &intruder_cell = mp_layout->cell (inst->object ().cell_index ()); - db::Box region = ref->box () & m_inst_bc (*inst); - if (! region.empty ()) { - - // @@@ TODO: should be lighter, cache, handle arrays .. - db::RecursiveShapeIterator si (*mp_layout, intruder_cell, m_intruder_layer, region); - si.shape_flags (db::ShapeIterator::PolygonRef); - while (! si.at_end ()) { - - // @@@ should be easier to transform references - const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); - db::Polygon poly = ref2->obj ().transformed (si.trans () * db::ICplxTrans (ref->trans ())); - (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_layout->shape_repository())); - - ++si; - - } - - } - } - -private: - db::Layout *mp_layout; - unsigned int m_intruder_layer; - db::box_convert m_inst_bc; - std::map > *mp_result; -}; - -struct InteractionRegistrationInst2Inst - : db::box_scanner_receiver2 -{ -public: - InteractionRegistrationInst2Inst (std::map, std::set > > *result) - : mp_result (result) - { - // nothing yet .. - } - - void add (const db::CellInstArray *inst1, int, const db::CellInstArray *inst2, int) - { - (*mp_result) [inst1].first.insert (inst2); - } - -private: - std::map, std::set > > *mp_result; -}; - -struct InteractionRegistrationInst2Shape - : db::box_scanner_receiver2 -{ -public: - InteractionRegistrationInst2Shape (std::map, std::set > > *result) - : mp_result (result) - { - // nothing yet .. - } - - void add (const db::CellInstArray *inst, int, const db::PolygonRef *ref, int) - { - (*mp_result) [inst].second.insert (*ref); - } - -private: - std::map, std::set > > *mp_result; -}; - -} - -// --------------------------------------------------------------------------------------------- - -LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer) - : mp_layout (layout), mp_top (top), m_scope_layer (scope_layer), m_intruder_layer (intruder_layer), m_output_layer (output_layer), mp_op (op) -{ - // .. nothing yet .. -} - -void LocalProcessor::run () -{ - try { - - mp_layout->update (); - mp_layout->start_changes (); - - std::pair, std::set > intruders; - compute_contexts (0, 0, mp_top, db::ICplxTrans (), intruders); - compute_results (); - - mp_layout->end_changes (); - - } catch (...) { - mp_layout->end_changes (); - throw; - } -} - -void LocalProcessor::push_results (db::Cell *cell, const std::set &result) -{ - if (! result.empty ()) { - cell->shapes (m_output_layer).insert (result.begin (), result.end ()); - } -} - -void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders) -{ - db::LocalProcessorCellContexts &contexts = m_contexts_per_cell [cell]; - - db::LocalProcessorCellContext *context = contexts.find_context (intruders); - if (context) { - context->add (parent_context, parent, cell_inst); - return; - } - - context = contexts.create (intruders); - context->add (parent_context, parent, cell_inst); - - const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); - - db::box_convert inst_bcs (*mp_layout, m_scope_layer); - db::box_convert inst_bci (*mp_layout, m_intruder_layer); - db::box_convert inst_bcii (*mp_layout, m_intruder_layer); - - if (! cell->begin ().at_end ()) { - - typedef std::pair, std::set > interaction_value_type; - - std::map interactions; - - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ())); - } - - { - db::box_scanner2 scanner; - InteractionRegistrationInst2Inst rec (&interactions); - - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - scanner.insert1 (&i->cell_inst (), 0); - scanner.insert2 (&i->cell_inst (), 0); - } - - for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); - } - - scanner.process (rec, 0, inst_bcs, inst_bci); - } - - { - db::box_scanner2 scanner; - InteractionRegistrationInst2Shape rec (&interactions); - - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - scanner.insert1 (&i->cell_inst (), 0); - } - - for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); - } - for (db::Shapes::shape_iterator i = shapes_intruders.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { - scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); - } - - scanner.process (rec, 0, inst_bcs, db::box_convert ()); - } - - for (std::map, std::set > >::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { - - std::pair, std::set > intruders_below; - intruders_below.second = i->second.second; - - db::Cell &child_cell = mp_layout->cell (i->first->object ().cell_index ()); - - for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) { - - db::ICplxTrans tn = i->first->complex_trans (*n); - db::ICplxTrans tni = tn.inverted (); - db::Box nbox = tn * child_cell.bbox (m_intruder_layer); - - if (! nbox.empty ()) { - - intruders_below.first.clear (); - - // @@@ TODO: in some cases, it may be possible to optimize this for arrays - - for (std::set::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) { - for (db::CellInstArray::iterator k = (*j)->begin_touching (nbox.enlarged (db::Vector (-1, -1)), inst_bcii); ! k.at_end (); ++k) { - intruders_below.first.insert (db::CellInstArray (db::CellInst ((*j)->object ().cell_index ()), tni * (*j)->complex_trans (*k))); - } - } - - compute_contexts (context, cell, &child_cell, tn, intruders_below); - - } - - } - - } - - } -} - -void -LocalProcessor::compute_results () -{ - for (db::Layout::bottom_up_const_iterator bu = mp_layout->begin_bottom_up (); bu != mp_layout->end_bottom_up (); ++bu) { - - contexts_per_cell_type::iterator cpc = m_contexts_per_cell.find (&mp_layout->cell (*bu)); - if (cpc != m_contexts_per_cell.end ()) { - cpc->second.compute_results (cpc->first, this); - m_contexts_per_cell.erase (cpc); - } - - } - -} - -void -LocalProcessor::compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const -{ - const db::Shapes &shapes_scope = cell->shapes (m_scope_layer); - const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); - - // local shapes vs. child cell - - std::map > interactions; - db::box_convert inst_bci (*mp_layout, m_intruder_layer); - - for (db::Shapes::shape_iterator i = shapes_scope.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { - interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); - } - - if (! shapes_scope.empty () && ! (shapes_intruders.empty () && intruders.second.empty ())) { - - db::box_scanner2 scanner; - InteractionRegistrationShape2Shape rec (&interactions); - - for (db::Shapes::shape_iterator i = shapes_scope.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { - scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); - } - - for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); - } - for (db::Shapes::shape_iterator i = shapes_intruders.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { - scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); - } - - scanner.process (rec, 0, db::box_convert (), db::box_convert ()); - - } - - if (! shapes_scope.empty () && ! (cell->begin ().at_end () && intruders.first.empty ())) { - - db::box_scanner2 scanner; - InteractionRegistrationShape2Inst rec (mp_layout, m_intruder_layer, &interactions); - - for (db::Shapes::shape_iterator i = shapes_scope.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { - scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); - } - - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - scanner.insert2 (&i->cell_inst (), 0); - } - for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); - } - - scanner.process (rec, 0, db::box_convert (), inst_bci); - - } - - mp_op->compute_local (mp_layout, interactions, result); -} - -// ------------------------------------------------------------------------------------------ - NetExtractor::NetExtractor() : mp_orig_layout (0), mp_layout (0) { diff --git a/src/plugins/tools/netx/db_plugin/db_plugin.pro b/src/plugins/tools/netx/db_plugin/db_plugin.pro index 9126b6af1..089fef33e 100644 --- a/src/plugins/tools/netx/db_plugin/db_plugin.pro +++ b/src/plugins/tools/netx/db_plugin/db_plugin.pro @@ -6,9 +6,11 @@ include($$PWD/../../../db_plugin.pri) HEADERS = \ dbNetExtractor.h \ + dbHierProcessor.h \ SOURCES = \ dbNetExtractor.cc \ + dbHierProcessor.cc \ dbNetExtractorPlugin.cc \ gsiDeclDbNetExtractor.cc \ diff --git a/src/plugins/tools/netx/testdata/hlp1.oas b/src/plugins/tools/netx/testdata/hlp1.oas new file mode 100644 index 0000000000000000000000000000000000000000..17754711a3ba196f11516e58c629422d70438e83 GIT binary patch literal 583 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4AcB`cq?(bTL2`x21eqJMA{8G* zX9zYNW%{5eQn5q)gXoKYOgqFyYOaW_5P48`LzbK2jG};uCDR3s4b4mpA_WbSJ)%JB zgXj)%peAH36>s?7s6RN!$RHB%Kz@~#Z2Y5b+ zg6tPH0I52_1JaPn$h1M}fi~DNqBbD$7wkWF0(EmUJP;Cqs9d4@pbW@jIl%J~sQ-X! MgAr51NCpfH0QW-V3jhEB literal 0 HcmV?d00001 diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc new file mode 100644 index 000000000..b06beba68 --- /dev/null +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -0,0 +1,112 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "tlStream.h" +#include "dbHierProcessor.h" +#include "dbTestSupport.h" +#include "dbReader.h" +#include "dbCommonReader.h" + +static std::string testdata (const std::string &fn) +{ + return tl::testsrc () + "/src/plugins/tools/netx/testdata/" + fn; +} + +enum TestMode +{ + TMAnd = 0, + TMNot = 1 +}; + +/** + * @brief Turns a layer into polygons and polygon references + * The hierarchical processor needs polygon references and can't work on polygons directly. + */ +void normalize_layer (db::Layout &layout, unsigned int layer) +{ + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + db::Shapes s; + s.swap (c->shapes (layer)); + for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); !i.at_end (); ++i) { + db::Polygon poly; + i->polygon (poly); + c->shapes (layer).insert (db::PolygonRef (poly, layout.shape_repository ())); + } + } +} + +void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num) +{ + db::Layout layout_org; + + unsigned int l1 = 0, l2 = 0, lout = 0; + db::LayerMap lmap; + + { + tl::InputStream stream (testdata (file)); + db::Reader reader (stream); + + db::LayerProperties p; + + p.layer = 1; + p.datatype = 0; + lmap.map (db::LDPair (1, 0), l1 = layout_org.insert_layer ()); + layout_org.set_properties (l1, p); + + p.layer = 2; + p.datatype = 0; + lmap.map (db::LDPair (2, 0), l2 = layout_org.insert_layer ()); + layout_org.set_properties (l2, p); + + p.layer = out_layer_num; + p.datatype = 0; + lmap.map (db::LDPair (out_layer_num, 0), lout = layout_org.insert_layer ()); + layout_org.set_properties (lout, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (layout_org, options); + } + + layout_org.clear_layer (lout); + normalize_layer (layout_org, l1); + normalize_layer (layout_org, l2); + + db::BoolAndOrNotLocalOperation op (mode == TMAnd); + db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &op, l1, l2, lout); + proc.run (); + + db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); +} + +TEST(BasicAnd1) +{ + run_test_bool (_this, "hlp1.oas", TMAnd, 100); +} + +TEST(BasicNot1) +{ + run_test_bool (_this, "hlp1.oas", TMNot, 101); +} diff --git a/src/plugins/tools/netx/unit_tests/unit_tests.pro b/src/plugins/tools/netx/unit_tests/unit_tests.pro index 5f51b3fff..a5e77b073 100644 --- a/src/plugins/tools/netx/unit_tests/unit_tests.pro +++ b/src/plugins/tools/netx/unit_tests/unit_tests.pro @@ -7,6 +7,7 @@ include($$PWD/../../../../lib_ut.pri) SOURCES = \ dbNetExtractorTests.cc \ + dbHierProcessorTests.cc \ INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common From 3cbbe041aa1a87c8bad2bc92f4ccc1eb61fc114d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Sep 2018 23:37:23 +0200 Subject: [PATCH 005/335] WIP: some bugfixes, new tests (up/down, down/up interactions) --- .../tools/netx/db_plugin/dbHierProcessor.cc | 48 +++++++++++------- src/plugins/tools/netx/testdata/hlp2.oas | Bin 0 -> 827 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 10 ++++ 3 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 src/plugins/tools/netx/testdata/hlp2.oas diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index c84e9fae0..76ae117c2 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -268,7 +268,7 @@ struct InteractionRegistrationShape2Inst { public: InteractionRegistrationShape2Inst (db::Layout *layout, unsigned int intruder_layer, std::map > *result) - : mp_layout (layout), m_intruder_layer (intruder_layer), m_inst_bc (*layout, intruder_layer), mp_result (result) + : mp_layout (layout), m_intruder_layer (intruder_layer), mp_result (result) { // nothing yet .. } @@ -276,30 +276,37 @@ public: void add (const db::PolygonRef *ref, int, const db::CellInstArray *inst, int) { const db::Cell &intruder_cell = mp_layout->cell (inst->object ().cell_index ()); - db::Box region = ref->box () & m_inst_bc (*inst); - if (! region.empty ()) { - // @@@ TODO: should be lighter, cache, handle arrays .. - db::RecursiveShapeIterator si (*mp_layout, intruder_cell, m_intruder_layer, region); - si.shape_flags (polygon_ref_flags ()); - while (! si.at_end ()) { + for (db::CellInstArray::iterator n = inst->begin (); !n.at_end (); ++n) { - // @@@ should be easier to transform references - const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); - db::Polygon poly = ref2->obj ().transformed (si.trans () * db::ICplxTrans (ref->trans ())); - (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_layout->shape_repository())); + db::ICplxTrans tn = inst->complex_trans (*n); - ++si; + db::Box region = ref->box ().transformed (tn.inverted ()) & intruder_cell.bbox (m_intruder_layer); + if (! region.empty ()) { + + // @@@ TODO: should be lighter, cache, handle arrays .. + db::RecursiveShapeIterator si (*mp_layout, intruder_cell, m_intruder_layer, region); + si.shape_flags (polygon_ref_flags ()); + while (! si.at_end ()) { + + // @@@ should be easier to transform references + const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); + db::Polygon poly = ref2->obj ().transformed (tn * si.trans () * db::ICplxTrans (ref2->trans ())); + (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_layout->shape_repository())); + + ++si; + + } } } + } private: db::Layout *mp_layout; unsigned int m_intruder_layer; - db::box_convert m_inst_bc; std::map > *mp_result; }; @@ -444,20 +451,25 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con for (std::map, std::set > >::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { - std::pair, std::set > intruders_below; - intruders_below.second = i->second.second; - db::Cell &child_cell = mp_layout->cell (i->first->object ().cell_index ()); for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) { db::ICplxTrans tn = i->first->complex_trans (*n); db::ICplxTrans tni = tn.inverted (); - db::Box nbox = tn * child_cell.bbox (m_intruder_layer); + db::Box nbox = tn * child_cell.bbox (m_scope_layer); if (! nbox.empty ()) { - intruders_below.first.clear (); + std::pair, std::set > intruders_below; + + // @@@ transformation of polygon refs - can this be done more efficiently? + for (std::set::const_iterator p = i->second.second.begin (); p != i->second.second.end (); ++p) { + if (nbox.overlaps (p->box ())) { + db::Polygon poly = p->obj ().transformed (tni * db::ICplxTrans (p->trans ())); + intruders_below.second.insert (db::PolygonRef (poly, mp_layout->shape_repository ())); + } + } // @@@ TODO: in some cases, it may be possible to optimize this for arrays diff --git a/src/plugins/tools/netx/testdata/hlp2.oas b/src/plugins/tools/netx/testdata/hlp2.oas new file mode 100644 index 0000000000000000000000000000000000000000..65b25366b7a8eecb71d9b478800a339743ba661b GIT binary patch literal 827 zcmd^5F-rq67|oX}RkSBo5=-SeC=?57u}%sur9J9Ft>RS_ms@laq>~g>IB-?yP(cTw zi`cPa-A>TW;RKP6hf{|NadQwcxmfMsKQKf1$oJmMdoOP4%f`$~+B8gc0F+d67Pb3O z@6qU(X)UKWEz`&q^sHqRih538vo?%uaj8F`shV7`7ybfhbilNVE2~yU&*wu5p911S zfna9I$Y)dVJ8yCE&$wHB3OJ!RTG5<1qcpWG7Oj|j1#})T7f1?uF*_kfQ7tH` zPRNO?D9oUuHLDP2O6M8e1UZlwr*Q21^!8gZ-zQn%zcr4reI)BZ zvJB4p Date: Sat, 22 Sep 2018 23:44:24 +0200 Subject: [PATCH 006/335] WIP: hierarchy variant building, tests --- src/plugins/tools/netx/testdata/hlp3.oas | Bin 0 -> 649 bytes .../tools/netx/unit_tests/dbHierProcessorTests.cc | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 src/plugins/tools/netx/testdata/hlp3.oas diff --git a/src/plugins/tools/netx/testdata/hlp3.oas b/src/plugins/tools/netx/testdata/hlp3.oas new file mode 100644 index 0000000000000000000000000000000000000000..765d95764f3cf57d0d0dfe60aa849bf9cf510673 GIT binary patch literal 649 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4V1mur!_&vbke5*;n~|YGa)rnO zHj#=CqB8_PtYF$9E>f{W{DbI?M@$SNHCMz|h+K$Tz{bsRMo~c2GL?~OgVF!QPhHw38+O6WFUxrLE`|=0o4U; zybOXr7$?Z=5a(t%U?(I}&ctvgKe@OWQe+n}ij;g1-67s^lnJ7) z2IROG|A6iUI`0kN8}$bV85tNvLLLZRaAW|vkmUf+N6`r~2UHu3Zp;GN4ixyw1rq2` fzaa~D*#Vv(Kmkz=My3NiK%dTHWNH}6fPn!3&_(k+ literal 0 HcmV?d00001 diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index 69780916d..0ba47241e 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -120,3 +120,13 @@ TEST(BasicNot2) { run_test_bool (_this, "hlp2.oas", TMNot, 101); } + +TEST(BasicAnd3) +{ + run_test_bool (_this, "hlp3.oas", TMAnd, 100); +} + +TEST(BasicNot3) +{ + run_test_bool (_this, "hlp3.oas", TMNot, 101); +} From 2595f4ed6bcf5abd5b155e60fa4985cff41b4b94 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Sep 2018 00:37:27 +0200 Subject: [PATCH 007/335] WIP: more test coverage. --- src/plugins/tools/netx/testdata/hlp4.oas | Bin 0 -> 667 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/plugins/tools/netx/testdata/hlp4.oas diff --git a/src/plugins/tools/netx/testdata/hlp4.oas b/src/plugins/tools/netx/testdata/hlp4.oas new file mode 100644 index 0000000000000000000000000000000000000000..0e43780b40094afce9ccba2545d64f650b3e6f22 GIT binary patch literal 667 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>FaudEAcBE~&Dq1#$HkBl$}nQ#Wf4hd zVrY=;5#?q`kzK$jQu2oHjrxr)i$SEEiQ$Ua1&s~OK+8)$i0%+?I0|wG$U!gu0o}#`as$f&o{ypvWDck{ s7~Pl!G9M`LlM5u!p?*UaZ1(}4A3y<74MwH|JV3w9Vq|I<$$)_Y08^9o>;M1& literal 0 HcmV?d00001 diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index 0ba47241e..f95c059d3 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -103,30 +103,48 @@ void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int ou TEST(BasicAnd1) { + // Simple flat AND run_test_bool (_this, "hlp1.oas", TMAnd, 100); } TEST(BasicNot1) { + // Simple flat NOT run_test_bool (_this, "hlp1.oas", TMNot, 101); } TEST(BasicAnd2) { + // Up/down and down/up interactions, AND run_test_bool (_this, "hlp2.oas", TMAnd, 100); } TEST(BasicNot2) { + // Up/down and down/up interactions, NOT run_test_bool (_this, "hlp2.oas", TMNot, 101); } TEST(BasicAnd3) { + // Variant building, AND run_test_bool (_this, "hlp3.oas", TMAnd, 100); } TEST(BasicNot3) { + // Variant building, NOT run_test_bool (_this, "hlp3.oas", TMNot, 101); } + +TEST(BasicAnd4) +{ + // Sibling interactions, variant building, AND + run_test_bool (_this, "hlp4.oas", TMAnd, 100); +} + +TEST(BasicNot4) +{ + // Sibling interactions, variant building, NOT + run_test_bool (_this, "hlp4.oas", TMNot, 101); +} From 8420b29025aa41a6278b7b81f50abe4ec08d72c4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Sep 2018 00:53:19 +0200 Subject: [PATCH 008/335] WIP: mixed propagation sample. --- src/klayout.pri | 5 +++++ .../tools/netx/unit_tests/dbHierProcessorTests.cc | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/klayout.pri b/src/klayout.pri index cc76f9e3a..07c8a0c41 100644 --- a/src/klayout.pri +++ b/src/klayout.pri @@ -111,6 +111,11 @@ msvc { } else { + CONFIG(gcov) { + QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage + QMAKE_LFLAGS += -fprofile-arcs -ftest-coverage + } + QMAKE_CXXFLAGS_WARN_ON += \ -pedantic \ -Woverloaded-virtual \ diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index f95c059d3..d7bd48367 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -148,3 +148,15 @@ TEST(BasicNot4) // Sibling interactions, variant building, NOT run_test_bool (_this, "hlp4.oas", TMNot, 101); } + +TEST(BasicAnd5) +{ + // Variant building with intermediate hierarchy, AND + run_test_bool (_this, "hlp5.oas", TMAnd, 100); +} + +TEST(BasicNot5) +{ + // Variant building with intermediate hierarchy, NOT + run_test_bool (_this, "hlp5.oas", TMNot, 101); +} From d7099edcae228bde931e3620f365fc323505c392 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Sep 2018 09:29:05 +0200 Subject: [PATCH 009/335] WIP: gcov enabled, added more test cases. --- src/klayout.pri | 2 +- src/plugins/tools/netx/testdata/hlp5.oas | Bin 0 -> 763 bytes src/plugins/tools/netx/testdata/hlp6.oas | Bin 0 -> 969 bytes src/plugins/tools/netx/testdata/hlp7.oas | Bin 0 -> 693 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 24 ++++++++++++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/plugins/tools/netx/testdata/hlp5.oas create mode 100644 src/plugins/tools/netx/testdata/hlp6.oas create mode 100644 src/plugins/tools/netx/testdata/hlp7.oas diff --git a/src/klayout.pri b/src/klayout.pri index 07c8a0c41..298273ebc 100644 --- a/src/klayout.pri +++ b/src/klayout.pri @@ -113,7 +113,7 @@ msvc { CONFIG(gcov) { QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage - QMAKE_LFLAGS += -fprofile-arcs -ftest-coverage + QMAKE_LFLAGS += --coverage } QMAKE_CXXFLAGS_WARN_ON += \ diff --git a/src/plugins/tools/netx/testdata/hlp5.oas b/src/plugins/tools/netx/testdata/hlp5.oas new file mode 100644 index 0000000000000000000000000000000000000000..709b3a865b55b25c7f1001afe39bd338ee7df480 GIT binary patch literal 763 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4Y|b8@J}!pLU=Dk5s1rzvmq8?( zk)c6yg~$Rnk%|wZGXy`ZVA>%rQn5q)gXoP%ObjA5SHxC`T!>o0#?5d>Q9#r(m62(K z(gSUu^Mk8HT~vdS=>QMVk+T>@YMHKx0d4JLVqg#{X^`v@W#DE=kzK$jQu0A`hxm{4 zOkndxYCxWP(8;_*9O$h#d~eimoM8m&2zelM!iRyEQSbx91epahnFK#D{p4ambxniO zf%!~O+dlx^CsGNvaziuIi$*4q0f#L!8G|UC+X)IC3f|D+jYk(VbU48wUBvh98g=kL@J;uAzwh^bzwf&@Yt~nd z`L(KPm>C&Ky9meFk0m+N<|yGE_Gaz3GDw79*|{1-r@ zvT3!})~%&_qmh>Qmy}#uF&ee96kSs0#-eCtfIL88&kTr@B%P4H%!UK(=19lQg_Ip* zAl;+s0rBrSNd*qCOl5F(486RYQ^32W(%bg|;o;R_Zu}L8MW71^`-M>|{EV2cU?*9+ zNJ{jYC=TY?9*T#qqlB0KPWzfSJ6>R~5seC`vMAUUrqgm8VJ|(Fd--^l>Ff|ir<$U}TI%>|!f0J3e*>{C#ntviTpht$pSO`|nPS6Gb6 zNcZHzNnVHmX$9&*12TyF>}$>|te_v#3w(^SAwHFX-V~0-8l#`seVP!;!oU3mvmqzm z-&H3ej6Wjp4KD@i)FgsnM?vj(mxkwP=}!iBMR@TGcBQYe=Lvd)yY~xB4?c-`!FDDm zmVx(+s(_zqdLN`o5InsYEwT=F25+V4hsE4p+OLZCZz0|$yV&`L+!FYYCDIX*4Ikmr k9wu8+W@JmG#eKzZf!pQ!q$7gjn+e={p!!v8|IY{D7cR75hyVZp literal 0 HcmV?d00001 diff --git a/src/plugins/tools/netx/testdata/hlp7.oas b/src/plugins/tools/netx/testdata/hlp7.oas new file mode 100644 index 0000000000000000000000000000000000000000..a167833a837033f9f79d6445be8fe47e52ef7042 GIT binary patch literal 693 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4Y|b8@J}!pLU=FLZf0%0!FOx_y z6T=m;3mO}mnM4X2Bzr^|L<&BL?htP{$|O>;L;Qp2i+@Zz#6>FJ@V!xgaFCHfB;bM2 z1xH>6k!(hW2FVp73)n;|K8Vf`{ICM745Z`6BPIrsnk!-}L@q=vVB=;uqbMM1naaqt zLFs`uSed8|Bhv+q7wj+QGJ@nE2nmQ9fJ9d4UWfv+SPt-f6rCV*K=p&Ds9_37;)d*n zrHrB$j7&fS^gzag*avt%h%zi-gJ?a#b3m1sQSb-D1eqP;+{`c7H?Z_G3NUS85&XgQ zVG#=hH^TutA(3*pU%459d?t|+u#Xu;N}yf_x*P6iARFds29XeuuNjycMlxVv000zI B0h<5- literal 0 HcmV?d00001 diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index d7bd48367..5d82fc840 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -160,3 +160,27 @@ TEST(BasicNot5) // Variant building with intermediate hierarchy, NOT run_test_bool (_this, "hlp5.oas", TMNot, 101); } + +TEST(BasicAnd6) +{ + // Extreme variants (copy, vanishing), AND + run_test_bool (_this, "hlp6.oas", TMAnd, 100); +} + +TEST(BasicNot6) +{ + // Extreme variants (copy, vanishing), NOT + run_test_bool (_this, "hlp6.oas", TMNot, 101); +} + +TEST(BasicAnd7) +{ + // Context replication - direct and indirect, AND + run_test_bool (_this, "hlp7.oas", TMAnd, 100); +} + +TEST(BasicNot7) +{ + // Context replication - direct and indirect, NOT + run_test_bool (_this, "hlp7.oas", TMNot, 101); +} From c1b08926418ff93e5131a78ce1ea476ec94e43ad Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Sep 2018 16:08:00 +0200 Subject: [PATCH 010/335] WIP: More testcases, better test coverage, some bug fixes --- .../tools/netx/db_plugin/dbHierProcessor.cc | 30 ++++----- .../tools/netx/db_plugin/dbHierProcessor.h | 28 +++++++- src/plugins/tools/netx/testdata/hlp8.oas | Bin 0 -> 815 bytes src/plugins/tools/netx/testdata/hlp9.oas | Bin 0 -> 724 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 61 +++++++++++++++++- 5 files changed, 99 insertions(+), 20 deletions(-) create mode 100644 src/plugins/tools/netx/testdata/hlp8.oas create mode 100644 src/plugins/tools/netx/testdata/hlp9.oas diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 76ae117c2..897d12094 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -322,7 +322,10 @@ public: void add (const db::CellInstArray *inst1, int, const db::CellInstArray *inst2, int) { - (*mp_result) [inst1].first.insert (inst2); + // @@@ TODO: always insert, if both instances come from different layouts + if (*inst1 != *inst2) { + (*mp_result) [inst1].first.insert (inst2); + } } private: @@ -361,21 +364,8 @@ LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperatio void LocalProcessor::run () { - try { - - mp_layout->update (); - mp_layout->start_changes (); - - std::pair, std::set > intruders; - compute_contexts (0, 0, mp_top, db::ICplxTrans (), intruders); - compute_results (); - - mp_layout->end_changes (); - - } catch (...) { - mp_layout->end_changes (); - throw; - } + compute_contexts (); + compute_results (); } void LocalProcessor::push_results (db::Cell *cell, const std::set &result) @@ -385,6 +375,14 @@ void LocalProcessor::push_results (db::Cell *cell, const std::set, std::set > intruders; + compute_contexts (0, 0, mp_top, db::ICplxTrans (), intruders); +} + void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders) { db::LocalProcessorCellContexts &contexts = m_contexts_per_cell [cell]; diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index e8b6707ee..743139832 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -89,6 +89,11 @@ public: return m_propagated; } + size_t size () const + { + return m_drops.size (); + } + private: std::set m_propagated; std::vector m_drops; @@ -98,6 +103,8 @@ class DB_PLUGIN_PUBLIC LocalProcessorCellContexts { public: typedef std::pair, std::set > key_type; + typedef std::map map_type; + typedef map_type::const_iterator iterator; LocalProcessorCellContexts (); @@ -105,6 +112,16 @@ public: db::LocalProcessorCellContext *create (const key_type &intruders); void compute_results (db::Cell *cell, LocalProcessor *proc); + iterator begin () const + { + return m_contexts.begin (); + } + + iterator end () const + { + return m_contexts.end (); + } + private: std::map m_contexts; }; @@ -112,12 +129,20 @@ private: class DB_PLUGIN_PUBLIC LocalProcessor { public: + typedef std::map contexts_per_cell_type; + LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer); void run (); + void compute_contexts (); + void compute_results (); + + const contexts_per_cell_type &contexts_per_cell () const + { + return m_contexts_per_cell; + } private: friend class LocalProcessorCellContexts; - typedef std::map contexts_per_cell_type; db::Layout *mp_layout; db::Cell *mp_top; @@ -126,7 +151,6 @@ private: LocalOperation *mp_op; void compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders); - void compute_results (); void push_results (db::Cell *cell, const std::set &result); void compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const; }; diff --git a/src/plugins/tools/netx/testdata/hlp8.oas b/src/plugins/tools/netx/testdata/hlp8.oas new file mode 100644 index 0000000000000000000000000000000000000000..27ca4eb75fd2088524110bed946c4b7a336d0ba0 GIT binary patch literal 815 zcmd^*ze^)Q6vyA2jhi#Ot0v4CBAeobU_caOb#f}iWYLwFh#P;9W*e<+1T9i1EO+e{ za@8RSK@d`EmvSu*uCSQGV#{Ea!eZ^T5oh*PEc_3A)thWl$4H=3 zn4E%M9Nw47MjUsgxaB(bV$~|UcC~ITT5Im6U9K;EiJ6A^f4pA(C}6ULkBer5V}v`W|-$MeiFLpWqvWBdm-zjy}yJuJL!=1 zo=7K6n|A|uW8EtRy4p?gq5*Ul!OOI1OQp?_{Yi%HC?;P7w*=_n&2XFYMoZP;`s)yz z*+FtK+8vK#Y!(U6_u{=%l=E(Ch)v0x_5^RR!nSOKEI&u~4T*R%DS@)*d0+Q)E!9wx z4{&UD(i-^lspJC@{@C{&z?iS!vt!fGdDB6bGZN+Bior4 flE=g{J$TWpMeFF{n`9yF)iTh!)C|P?%mwfa9jqtd literal 0 HcmV?d00001 diff --git a/src/plugins/tools/netx/testdata/hlp9.oas b/src/plugins/tools/netx/testdata/hlp9.oas new file mode 100644 index 0000000000000000000000000000000000000000..3baf603e09e750fea443d8d612e6402edb772029 GIT binary patch literal 724 zcmd^*ze~eF6vy9PN-Cz&q}*965r06iAc}QT5TV#m1GN=XQCzar$<;wnF(5cObnKuI zL7{^~QHPFk5ELA4a8Ph4;t<3kZh|0Q+Qq^Dz&CyGz3=<-KBrs8f;m>qSf(X+A|X9K z0e%#Imqz<6dm*!GTV}3cWNouhGIGX}y<%od^BoaQ*4pFc!e0iBc3O6+xM^ zN)YGf&3ra3v>uTqvyhW9*+cMpj_*5M(T_^-e1hUsFC$!4Z=WJSb}{R&Tp$1ms;5{X zW&aXiDQ%w&WCMYdtPxivTLG=AK@B$&WUG+~=;I3pvh&bWBW%qTCAc3_==3_m_2K4d z%SLAqW+^}FAER*Ij4N~^?9|5C8dni7_CYI}rw5d#`8_Q4*w4-ookExgjEC`|_F@4A zS8T6(F;AC~`Uw@Sk&wWfjjI8^%Rd##HUfXpNwPPget@XX?{Frossu;9N-N!&bO-z| zf#W{(uvC2?b6&nqHl5@l1t6ONZ6utV5GjQ7zd0w@?_KXB&1?QUq|}VF3}EL!7r+mO Cod>`G literal 0 HcmV?d00001 diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index 5d82fc840..2f195db78 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -56,7 +56,26 @@ void normalize_layer (db::Layout &layout, unsigned int layer) } } -void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num) + +std::string contexts_to_s (db::Layout *layout, const db::LocalProcessor::contexts_per_cell_type &contexts) +{ + std::string res; + + for (db::Layout::top_down_const_iterator i = layout->begin_top_down (); i != layout->end_top_down(); ++i) { + db::LocalProcessor::contexts_per_cell_type::const_iterator cc = contexts.find (&layout->cell (*i)); + if (cc != contexts.end ()) { + int index = 1; + for (db::LocalProcessorCellContexts::iterator j = cc->second.begin (); j != cc->second.end (); ++j) { + res += tl::sprintf ("%s[%d] %d insts, %d shapes (%d times)\n", layout->cell_name (*i), index, int (j->first.first.size ()), int (j->first.second.size ()), int (j->second.size ())); + index += 1; + } + } + } + + return res; +} + +void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0) { db::Layout layout_org; @@ -96,7 +115,14 @@ void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int ou db::BoolAndOrNotLocalOperation op (mode == TMAnd); db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &op, l1, l2, lout); - proc.run (); + + if (! context_doc) { + proc.run (); + } else { + proc.compute_contexts (); + *context_doc = contexts_to_s (&layout_org, proc.contexts_per_cell ()); + proc.compute_results (); + } db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); } @@ -184,3 +210,34 @@ TEST(BasicNot7) // Context replication - direct and indirect, NOT run_test_bool (_this, "hlp7.oas", TMNot, 101); } + +TEST(BasicAnd8) +{ + // Mixed sibling-parent contexts, AND + run_test_bool (_this, "hlp8.oas", TMAnd, 100); +} + +TEST(BasicNot8) +{ + // Mixed sibling-parent contexts, NOT + run_test_bool (_this, "hlp8.oas", TMNot, 101); +} + +TEST(BasicAnd9) +{ + // Top-level ring structure, AND + std::string doc; + run_test_bool (_this, "hlp9.oas", TMAnd, 100, &doc); + EXPECT_EQ (doc, + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 12 shapes (1 times)\n" + "CHILD1[1] 1 insts, 6 shapes (1 times)\n" + "CHILD1[2] 1 insts, 6 shapes (1 times)\n" + ); +} + +TEST(BasicNot9) +{ + // Top-level ring structure, NOT + run_test_bool (_this, "hlp9.oas", TMNot, 101); +} From 9e09002d438960ca39e788e5bdcea4f1516d55f0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Sep 2018 16:57:49 +0200 Subject: [PATCH 011/335] WIP: refined interaction testing. --- .../tools/netx/db_plugin/dbHierProcessor.cc | 128 +++++++++++++++--- .../tools/netx/db_plugin/dbHierProcessor.h | 4 +- .../netx/unit_tests/dbHierProcessorTests.cc | 21 ++- 3 files changed, 127 insertions(+), 26 deletions(-) diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 897d12094..e952f611f 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -276,8 +276,9 @@ public: void add (const db::PolygonRef *ref, int, const db::CellInstArray *inst, int) { const db::Cell &intruder_cell = mp_layout->cell (inst->object ().cell_index ()); + db::box_convert inst_bc (*mp_layout, m_intruder_layer); - for (db::CellInstArray::iterator n = inst->begin (); !n.at_end (); ++n) { + for (db::CellInstArray::iterator n = inst->begin_touching (ref->box ().enlarged (db::Vector (-1, -1)), inst_bc); !n.at_end (); ++n) { db::ICplxTrans tn = inst->complex_trans (*n); @@ -301,7 +302,6 @@ public: } } - } private: @@ -310,12 +310,65 @@ private: std::map > *mp_result; }; +static bool +instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, unsigned int layer1, const db::Layout *layout2, const db::CellInstArray *inst2, unsigned int layer2) +{ + const db::Cell &cell1 = layout1->cell (inst1->object ().cell_index ()); + const db::Cell &cell2 = layout2->cell (inst2->object ().cell_index ()); + db::box_convert inst2_bc (*layout2, layer2); + + std::set relative_trans_seen; + + for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) { + + db::ICplxTrans tn1 = inst1->complex_trans (*n); + db::ICplxTrans tni1 = tn1.inverted (); + db::Box ibox1 = tn1 * cell1.bbox (layer1); + + if (! ibox1.empty ()) { + + // @@@ TODO: in some cases, it may be possible to optimize this for arrays + + for (db::CellInstArray::iterator k = inst2->begin_touching (ibox1.enlarged (db::Vector (-1, -1)), inst2_bc); ! k.at_end (); ++k) { + + db::ICplxTrans tn2 = inst2->complex_trans (*k); + db::Box ibox2 = tn2 * cell2.bbox (layer2); + + db::ICplxTrans tn21 = tni1 * tn2; + if (! relative_trans_seen.insert (tn21).second) { + // this relative transformation was already seen + continue; + } + + db::Box cbox = ibox1 & ibox2; + if (! cbox.empty ()) { + + db::ICplxTrans tni2 = tn2.inverted (); + + // not very strong, but already useful: the cells interact if there is a layer1 in cell1 + // in the common box and a layer2 in the cell2 in the common box + if (! db::RecursiveShapeIterator (*layout1, cell1, layer1, tni1 * cbox, true).at_end () && + ! db::RecursiveShapeIterator (*layout2, cell2, layer2, tni2 * cbox, true).at_end ()) { + return true; + } + + } + + } + + } + + } + + return false; +} + struct InteractionRegistrationInst2Inst : db::box_scanner_receiver2 { public: - InteractionRegistrationInst2Inst (std::map, std::set > > *result) - : mp_result (result) + InteractionRegistrationInst2Inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, std::map, std::set > > *result) + : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), mp_result (result) { // nothing yet .. } @@ -323,31 +376,66 @@ public: void add (const db::CellInstArray *inst1, int, const db::CellInstArray *inst2, int) { // @@@ TODO: always insert, if both instances come from different layouts - if (*inst1 != *inst2) { + if (*inst1 != *inst2 && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer)) { (*mp_result) [inst1].first.insert (inst2); } } private: + const db::Layout *mp_subject_layout, *mp_intruder_layout; + unsigned int m_subject_layer, m_intruder_layer; std::map, std::set > > *mp_result; }; +static bool +instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *inst, unsigned int layer, const db::PolygonRef &ref) +{ + const db::Cell &cell = layout->cell (inst->object ().cell_index ()); + db::box_convert inst_bc (*layout, layer); + db::Box rbox = ref.box (); + + for (db::CellInstArray::iterator n = inst->begin_touching (rbox.enlarged (db::Vector (-1, -1)), inst_bc); ! n.at_end (); ++n) { + + db::ICplxTrans tn = inst->complex_trans (*n); + db::Box cbox = (tn * cell.bbox (layer)) & rbox; + + if (! cbox.empty ()) { + + db::ICplxTrans tni = tn.inverted (); + + // not very strong, but already useful: the cells interact if there is a layer in cell + // in the common box + if (! db::RecursiveShapeIterator (*layout, cell, layer, tni * cbox, true).at_end ()) { + return true; + } + + } + + } + + return false; +} + struct InteractionRegistrationInst2Shape : db::box_scanner_receiver2 { public: - InteractionRegistrationInst2Shape (std::map, std::set > > *result) - : mp_result (result) + InteractionRegistrationInst2Shape (const db::Layout *subject_layout, unsigned int subject_layer, std::map, std::set > > *result) + : mp_subject_layout (subject_layout), m_subject_layer (subject_layer), mp_result (result) { // nothing yet .. } void add (const db::CellInstArray *inst, int, const db::PolygonRef *ref, int) { - (*mp_result) [inst].second.insert (*ref); + if (instance_shape_interacts (mp_subject_layout, inst, m_subject_layer, *ref)) { + (*mp_result) [inst].second.insert (*ref); + } } private: + const db::Layout *mp_subject_layout; + unsigned int m_subject_layer; std::map, std::set > > *mp_result; }; @@ -356,8 +444,8 @@ private: // --------------------------------------------------------------------------------------------- // LocalProcessor implementation -LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer) - : mp_layout (layout), mp_top (top), m_scope_layer (scope_layer), m_intruder_layer (intruder_layer), m_output_layer (output_layer), mp_op (op) +LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer) + : mp_layout (layout), mp_top (top), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_output_layer (output_layer), mp_op (op) { // .. nothing yet .. } @@ -398,7 +486,7 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); - db::box_convert inst_bcs (*mp_layout, m_scope_layer); + db::box_convert inst_bcs (*mp_layout, m_subject_layer); db::box_convert inst_bci (*mp_layout, m_intruder_layer); db::box_convert inst_bcii (*mp_layout, m_intruder_layer); @@ -415,7 +503,7 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con { db::box_scanner2 scanner; - InteractionRegistrationInst2Inst rec (&interactions); + InteractionRegistrationInst2Inst rec (mp_layout, m_subject_layer, mp_layout, m_intruder_layer, &interactions); for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { scanner.insert1 (&i->cell_inst (), 0); @@ -431,7 +519,7 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con { db::box_scanner2 scanner; - InteractionRegistrationInst2Shape rec (&interactions); + InteractionRegistrationInst2Shape rec (mp_layout, m_subject_layer, &interactions); for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { scanner.insert1 (&i->cell_inst (), 0); @@ -455,7 +543,7 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con db::ICplxTrans tn = i->first->complex_trans (*n); db::ICplxTrans tni = tn.inverted (); - db::Box nbox = tn * child_cell.bbox (m_scope_layer); + db::Box nbox = tn * child_cell.bbox (m_subject_layer); if (! nbox.empty ()) { @@ -506,7 +594,7 @@ LocalProcessor::compute_results () void LocalProcessor::compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const { - const db::Shapes &shapes_scope = cell->shapes (m_scope_layer); + const db::Shapes &shapes_subject = cell->shapes (m_subject_layer); const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); // local shapes vs. child cell @@ -515,16 +603,16 @@ LocalProcessor::compute_local_cell (db::Cell *cell, const std::pair inst_bci (*mp_layout, m_intruder_layer); // @@@ TODO: don't do this in AND mode (we don't need interactions without an intruder) - for (db::Shapes::shape_iterator i = shapes_scope.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); } - if (! shapes_scope.empty () && ! (shapes_intruders.empty () && intruders.second.empty ())) { + if (! shapes_subject.empty () && ! (shapes_intruders.empty () && intruders.second.empty ())) { db::box_scanner2 scanner; InteractionRegistrationShape2Shape rec (&interactions); - for (db::Shapes::shape_iterator i = shapes_scope.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); } @@ -539,12 +627,12 @@ LocalProcessor::compute_local_cell (db::Cell *cell, const std::pairbegin ().at_end () && intruders.first.empty ())) { + if (! shapes_subject.empty () && ! (cell->begin ().at_end () && intruders.first.empty ())) { db::box_scanner2 scanner; InteractionRegistrationShape2Inst rec (mp_layout, m_intruder_layer, &interactions); - for (db::Shapes::shape_iterator i = shapes_scope.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); } diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index 743139832..3522c7e8c 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -131,7 +131,7 @@ class DB_PLUGIN_PUBLIC LocalProcessor public: typedef std::map contexts_per_cell_type; - LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer); + LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer); void run (); void compute_contexts (); void compute_results (); @@ -146,7 +146,7 @@ private: db::Layout *mp_layout; db::Cell *mp_top; - unsigned int m_scope_layer, m_intruder_layer, m_output_layer; + unsigned int m_subject_layer, m_intruder_layer, m_output_layer; contexts_per_cell_type m_contexts_per_cell; LocalOperation *mp_op; diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index 2f195db78..aa9194039 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -229,15 +229,28 @@ TEST(BasicAnd9) std::string doc; run_test_bool (_this, "hlp9.oas", TMAnd, 100, &doc); EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 0 insts, 12 shapes (1 times)\n" - "CHILD1[1] 1 insts, 6 shapes (1 times)\n" - "CHILD1[2] 1 insts, 6 shapes (1 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 4 shapes (2 times)\n" ); } TEST(BasicNot9) { // Top-level ring structure, NOT - run_test_bool (_this, "hlp9.oas", TMNot, 101); + std::string doc; + run_test_bool (_this, "hlp9.oas", TMNot, 101, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + ); } From 3c15d8e38762036b698fbe7cbf1fc538d09f1225 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Sep 2018 17:22:08 +0200 Subject: [PATCH 012/335] WIP: some optimization with empty intruder hint. --- .../tools/netx/db_plugin/dbHierProcessor.cc | 16 ++++++++++++---- .../tools/netx/db_plugin/dbHierProcessor.h | 6 ++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index e952f611f..9f8948a1e 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -70,6 +70,12 @@ BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and) // .. nothing yet .. } +LocalOperation::on_empty_intruder_mode +BoolAndOrNotLocalOperation::on_empty_intruder_hint () const +{ + return m_is_and ? LocalOperation::Drop : LocalOperation::Copy; +} + void BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const { @@ -496,7 +502,7 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con std::map interactions; - // @@@ TODO: don't do this in AND more for instances without an intruder + // insert dummy interactions to handle at least the child cell vs. itself for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ())); } @@ -602,9 +608,11 @@ LocalProcessor::compute_local_cell (db::Cell *cell, const std::pair > interactions; db::box_convert inst_bci (*mp_layout, m_intruder_layer); - // @@@ TODO: don't do this in AND mode (we don't need interactions without an intruder) - for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { - interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); + if (mp_op->on_empty_intruder_hint () != LocalOperation::Drop) { + // insert dummy interactions to accommodate subject vs. nothing + for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); + } } if (! shapes_subject.empty () && ! (shapes_intruders.empty () && intruders.second.empty ())) { diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index 3522c7e8c..c97a2d7c3 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -41,10 +41,15 @@ class LocalProcessorCellContext; class DB_PLUGIN_PUBLIC LocalOperation { public: + enum on_empty_intruder_mode { + Ignore = 0, Copy, Drop + }; + LocalOperation () { } virtual ~LocalOperation () { } virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const = 0; + virtual on_empty_intruder_mode on_empty_intruder_hint () const = 0; }; class DB_PLUGIN_PUBLIC BoolAndOrNotLocalOperation @@ -54,6 +59,7 @@ public: BoolAndOrNotLocalOperation (bool is_and); virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const; + virtual on_empty_intruder_mode on_empty_intruder_hint () const; private: bool m_is_and; From 611a98165dd883e08c10d958dfeccd80245f23e4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Sep 2018 17:49:10 +0200 Subject: [PATCH 013/335] WIP: diagnostic output --- .../tools/netx/db_plugin/dbHierProcessor.cc | 35 +++++++++++++++++-- .../tools/netx/db_plugin/dbHierProcessor.h | 13 +++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 9f8948a1e..1e040b3b1 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -28,6 +28,8 @@ #include "dbEdgeProcessor.h" #include "dbPolygonGenerators.h" #include "tlLog.h" +#include "tlTimer.h" +#include "tlInternational.h" namespace db { @@ -76,6 +78,12 @@ BoolAndOrNotLocalOperation::on_empty_intruder_hint () const return m_is_and ? LocalOperation::Drop : LocalOperation::Copy; } +std::string +BoolAndOrNotLocalOperation::description () const +{ + return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); +} + void BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const { @@ -192,8 +200,16 @@ LocalProcessorCellContexts::compute_results (db::Cell *cell, LocalProcessor *pro bool first = true; std::set common; + int index = 0; + int total = int (m_contexts.size ()); for (std::map::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { + ++index; + + if (tl::verbosity () >= 30) { + tl::log << tr ("Computing local results for ") << cell->layout ()->cell_name (cell->cell_index ()) << " (context " << index << "/" << total << ")"; + } + if (first) { common = c->second.propagated (); @@ -453,7 +469,7 @@ private: LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer) : mp_layout (layout), mp_top (top), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_output_layer (output_layer), mp_op (op) { - // .. nothing yet .. + set_description (op->description ()); } void LocalProcessor::run () @@ -471,6 +487,8 @@ void LocalProcessor::push_results (db::Cell *cell, const std::set= 21, tl::to_string (tr ("Computing contexts for ")) + description ()); + m_contexts_per_cell.clear (); std::pair, std::set > intruders; @@ -479,6 +497,14 @@ void LocalProcessor::compute_contexts () void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders) { + if (tl::verbosity () >= 30) { + if (! parent) { + tl::log << tr ("Computing context for top cell ") << mp_layout->cell_name (cell->cell_index ()); + } else { + tl::log << tr ("Computing context for ") << mp_layout->cell_name (parent->cell_index ()) << " -> " << mp_layout->cell_name (cell->cell_index ()) << " @" << cell_inst.to_string (); + } + } + db::LocalProcessorCellContexts &contexts = m_contexts_per_cell [cell]; db::LocalProcessorCellContext *context = contexts.find_context (intruders); @@ -585,6 +611,12 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con void LocalProcessor::compute_results () { + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing results for ")) + description ()); + + // avoids updates while we work on the layout + mp_layout->update (); + db::LayoutLocker locker (mp_layout); + for (db::Layout::bottom_up_const_iterator bu = mp_layout->begin_bottom_up (); bu != mp_layout->end_bottom_up (); ++bu) { contexts_per_cell_type::iterator cpc = m_contexts_per_cell.find (&mp_layout->cell (*bu)); @@ -594,7 +626,6 @@ LocalProcessor::compute_results () } } - } void diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index c97a2d7c3..47d1e2478 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -50,6 +50,7 @@ public: virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const = 0; virtual on_empty_intruder_mode on_empty_intruder_hint () const = 0; + virtual std::string description () const = 0; }; class DB_PLUGIN_PUBLIC BoolAndOrNotLocalOperation @@ -60,6 +61,7 @@ public: virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const; virtual on_empty_intruder_mode on_empty_intruder_hint () const; + virtual std::string description () const; private: bool m_is_and; @@ -147,6 +149,16 @@ public: return m_contexts_per_cell; } + void set_description (const std::string &d) + { + m_description = d; + } + + const std::string &description () const + { + return m_description; + } + private: friend class LocalProcessorCellContexts; @@ -155,6 +167,7 @@ private: unsigned int m_subject_layer, m_intruder_layer, m_output_layer; contexts_per_cell_type m_contexts_per_cell; LocalOperation *mp_op; + std::string m_description; void compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders); void push_results (db::Cell *cell, const std::set &result); From 52a4459dacc0cdcad5f3b7d381a9f0b4cff600bc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Sep 2018 20:06:27 +0200 Subject: [PATCH 014/335] WIP: timer enhanced (reports start now), drop empty boxes in hier processor (will make the processor stall). --- .../tools/netx/db_plugin/dbHierProcessor.cc | 24 +++++++++---- .../tools/netx/db_plugin/dbNetExtractor.cc | 34 ++++++++++++++++--- .../tools/netx/db_plugin/dbNetExtractor.h | 4 +++ .../netx/db_plugin/gsiDeclDbNetExtractor.cc | 16 +++++++++ src/tl/tl/tlTimer.cc | 6 ++++ src/tl/tl/tlTimer.h | 3 ++ 6 files changed, 76 insertions(+), 11 deletions(-) diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 1e040b3b1..0d5ba4cea 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -538,12 +538,18 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con InteractionRegistrationInst2Inst rec (mp_layout, m_subject_layer, mp_layout, m_intruder_layer, &interactions); for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - scanner.insert1 (&i->cell_inst (), 0); - scanner.insert2 (&i->cell_inst (), 0); + if (! inst_bcs (i->cell_inst ()).empty ()) { + scanner.insert1 (&i->cell_inst (), 0); + } + if (! inst_bci (i->cell_inst ()).empty ()) { + scanner.insert2 (&i->cell_inst (), 0); + } } for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); + if (! inst_bci (*i).empty ()) { + scanner.insert2 (i.operator-> (), 0); + } } scanner.process (rec, 0, inst_bcs, inst_bci); @@ -554,7 +560,9 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con InteractionRegistrationInst2Shape rec (mp_layout, m_subject_layer, &interactions); for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - scanner.insert1 (&i->cell_inst (), 0); + if (! inst_bcs (i->cell_inst ()).empty ()) { + scanner.insert1 (&i->cell_inst (), 0); + } } for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { @@ -676,10 +684,14 @@ LocalProcessor::compute_local_cell (db::Cell *cell, const std::pairbegin (); !i.at_end (); ++i) { - scanner.insert2 (&i->cell_inst (), 0); + if (! inst_bci (i->cell_inst ()).empty ()) { + scanner.insert2 (&i->cell_inst (), 0); + } } for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); + if (! inst_bci (*i).empty ()) { + scanner.insert2 (i.operator-> (), 0); + } } scanner.process (rec, 0, db::box_convert (), inst_bci); diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc index fe0521e23..ea5357edf 100644 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc @@ -22,6 +22,7 @@ #include "dbNetExtractor.h" +#include "dbHierProcessor.h" #include "dbLayoutUtils.h" #include "dbCellMapping.h" #include "dbPolygonTools.h" @@ -34,7 +35,7 @@ namespace db { NetExtractor::NetExtractor() - : mp_orig_layout (0), mp_layout (0) + : mp_orig_layout (0), mp_layout (0), mp_top_cell (0) { // @@@ @@ -45,21 +46,30 @@ NetExtractor::~NetExtractor () { delete mp_layout; mp_layout = 0; + mp_top_cell = 0; } void NetExtractor::open (const db::Layout &orig_layout, cell_index_type orig_top_cell) { + tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Open layout"))); + delete mp_layout; mp_orig_layout = &orig_layout; mp_layout = new db::Layout (); mp_layout->dbu (orig_layout.dbu ()); - db::cell_index_type top = mp_layout->add_cell (orig_layout.cell_name (orig_top_cell)); + mp_top_cell = &mp_layout->cell (mp_layout->add_cell (orig_layout.cell_name (orig_top_cell))); // copy hierarchy m_cm.clear (); - m_cm.create_from_names_full (*mp_layout, top, orig_layout, orig_top_cell); + m_cm.create_from_names_full (*mp_layout, mp_top_cell->cell_index (), orig_layout, orig_top_cell); +} + +void +NetExtractor::output (NetLayer a, const LayerProperties &lp) +{ + mp_layout->set_properties (a.layer_index (), lp); } static double area_ratio (const db::Polygon &poly) @@ -92,6 +102,8 @@ static void split_polygon_into (const db::Polygon &poly, db::Shapes &dest, size_ NetLayer NetExtractor::load (unsigned int layer_index) { + tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Loading layer ")) + mp_orig_layout->get_properties (layer_index).to_string ()); + const double max_area_ratio = 3.0; const size_t max_points = 16; @@ -126,13 +138,25 @@ NetExtractor::load (unsigned int layer_index) NetLayer NetExtractor::bool_and (NetLayer a, NetLayer b) { - return NetLayer (mp_layout->insert_layer ()); // @@@ + return and_or_not (a, b, true); } NetLayer NetExtractor::bool_not (NetLayer a, NetLayer b) { - return NetLayer (mp_layout->insert_layer ()); // @@@ + return and_or_not (a, b, false); +} + +NetLayer +NetExtractor::and_or_not (NetLayer a, NetLayer b, bool is_and) +{ + unsigned int lout = mp_layout->insert_layer (); + + db::BoolAndOrNotLocalOperation op (is_and); + db::LocalProcessor proc (mp_layout, mp_top_cell, &op, a.layer_index (), b.layer_index (), lout); + proc.run (); + + return NetLayer (lout); } db::Layout * diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.h b/src/plugins/tools/netx/db_plugin/dbNetExtractor.h index 2f478e93f..d4066a3ae 100644 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.h +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.h @@ -71,6 +71,7 @@ public: NetLayer load (unsigned int layer_index); NetLayer bool_and (NetLayer a, NetLayer b); NetLayer bool_not (NetLayer a, NetLayer b); + void output (NetLayer a, const db::LayerProperties &lp); db::Layout *layout_copy () const; private: @@ -78,9 +79,12 @@ private: NetExtractor (const db::NetExtractor &); NetExtractor &operator= (const db::NetExtractor &); + NetLayer and_or_not (NetLayer a, NetLayer b, bool is_and); + // @@@ const db::Layout *mp_orig_layout; // @@@ should be a smart pointer db::Layout *mp_layout; + db::Cell *mp_top_cell; db::CellMapping m_cm; }; diff --git a/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc index a3a549902..d6ee3c1df 100644 --- a/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc +++ b/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc @@ -41,6 +41,16 @@ void open2 (db::NetExtractor *ex, const db::Layout *orig_layout, const db::Cell ex->open (*orig_layout, cell->cell_index ()); } +void output1 (db::NetExtractor *ex, const db::NetLayer &nl, const db::LayerProperties &lp) +{ + ex->output (nl, lp); +} + +void output2 (db::NetExtractor *ex, const db::NetLayer &nl, int layer, int datatype, const std::string &name) +{ + ex->output (nl, db::LayerProperties (layer, datatype, name)); +} + gsi::Class decl_NetNetExtractor ("db", "NetExtractor", gsi::method ("open", &db::NetExtractor::open, gsi::arg ("orig_layout"), gsi::arg ("orig_top_cell_index"), "@@@" @@ -48,6 +58,12 @@ gsi::Class decl_NetNetExtractor ("db", "NetExtractor", gsi::method_ext ("open", &open2, gsi::arg ("orig_layout"), gsi::arg ("orig_top_cell"), "@@@" ) + + gsi::method_ext ("output", &output1, gsi::arg ("net_layer"), gsi::arg ("layer"), + "@@@" + ) + + gsi::method_ext ("output", &output2, gsi::arg ("net_layer"), gsi::arg ("layer"), gsi::arg ("datatype"), gsi::arg ("name", std::string ()), + "@@@" + ) + gsi::method ("load", &db::NetExtractor::load, gsi::arg ("layer_index"), "@@@" ) + diff --git a/src/tl/tl/tlTimer.cc b/src/tl/tl/tlTimer.cc index 1be9d0b81..de04772ee 100644 --- a/src/tl/tl/tlTimer.cc +++ b/src/tl/tl/tlTimer.cc @@ -186,6 +186,12 @@ Timer::take () m_wall_ms = wall_ms; } +void +SelfTimer::start_report () const +{ + tl::info << m_desc << ": " << tl::to_string (tr ("started")); +} + void SelfTimer::report () const { diff --git a/src/tl/tl/tlTimer.h b/src/tl/tl/tlTimer.h index 2799e34af..14d3cbe3b 100644 --- a/src/tl/tl/tlTimer.h +++ b/src/tl/tl/tlTimer.h @@ -118,6 +118,7 @@ public: { m_enabled = true; start (); + start_report (); } /** @@ -131,6 +132,7 @@ public: m_enabled = enabled; if (enabled) { start (); + start_report (); } } @@ -144,6 +146,7 @@ public: private: void report () const; + void start_report () const; std::string m_desc; bool m_enabled; From eb71121c3841b4776e7bcf01c387c9608235a270 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Sep 2018 22:34:50 +0200 Subject: [PATCH 015/335] WIP: bugfix - intra-array self interactions. --- .../tools/netx/db_plugin/dbHierProcessor.cc | 12 +++++++++++- src/plugins/tools/netx/testdata/hlp10.oas | Bin 0 -> 478 bytes .../tools/netx/unit_tests/dbHierProcessorTests.cc | 13 +++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/plugins/tools/netx/testdata/hlp10.oas diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 0d5ba4cea..98b38edf3 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -335,6 +335,8 @@ private: static bool instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, unsigned int layer1, const db::Layout *layout2, const db::CellInstArray *inst2, unsigned int layer2) { + // TODO: this algorithm is not in particular effective for identical arrays + const db::Cell &cell1 = layout1->cell (inst1->object ().cell_index ()); const db::Cell &cell2 = layout2->cell (inst2->object ().cell_index ()); db::box_convert inst2_bc (*layout2, layer2); @@ -353,6 +355,11 @@ instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, u for (db::CellInstArray::iterator k = inst2->begin_touching (ibox1.enlarged (db::Vector (-1, -1)), inst2_bc); ! k.at_end (); ++k) { + if (inst1 == inst2 && *n == *k) { + // skip self-interactions - this is handled inside the cell + continue; + } + db::ICplxTrans tn2 = inst2->complex_trans (*k); db::Box ibox2 = tn2 * cell2.bbox (layer2); @@ -398,7 +405,10 @@ public: void add (const db::CellInstArray *inst1, int, const db::CellInstArray *inst2, int) { // @@@ TODO: always insert, if both instances come from different layouts - if (*inst1 != *inst2 && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer)) { + // NOTE: self-interactions are possible for arrays: different elements of the + // array may interact which is a cell-external interaction. + if ((*inst1 != *inst2 || inst1->size () > 1) + && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer)) { (*mp_result) [inst1].first.insert (inst2); } } diff --git a/src/plugins/tools/netx/testdata/hlp10.oas b/src/plugins/tools/netx/testdata/hlp10.oas new file mode 100644 index 0000000000000000000000000000000000000000..6ca1d9c163b2000b74dc80fb15ebd1463cbe0f3f GIT binary patch literal 478 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4V1m`z!_&uwmr>N1k%5t^L;a7~ zS7uR5CPty7yw=Fw(0-? literal 0 HcmV?d00001 diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index aa9194039..f035a547c 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -254,3 +254,16 @@ TEST(BasicNot9) "CHILD1[1] 0 insts, 4 shapes (2 times)\n" ); } + +TEST(BasicAnd10) +{ + // Array instances, AND + run_test_bool (_this, "hlp10.oas", TMAnd, 100); +} + +TEST(BasicNot10) +{ + // Array instances, NOT + run_test_bool (_this, "hlp10.oas", TMNot, 101); +} + From 824888aaa661063d253ed76087e6baf02d3bc596 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Sep 2018 22:25:40 +0200 Subject: [PATCH 016/335] WIP: some refactoring. --- .../tools/netx/db_plugin/dbHierProcessor.cc | 82 ++++++++-------- .../tools/netx/db_plugin/dbHierProcessor.h | 90 +++++++++++++++--- .../tools/netx/db_plugin/dbNetExtractor.cc | 4 +- src/plugins/tools/netx/testdata/hlp10.oas | Bin 478 -> 489 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 23 ++--- 5 files changed, 134 insertions(+), 65 deletions(-) diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 98b38edf3..b5e25f8b6 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -195,7 +195,7 @@ LocalProcessorCellContexts::create (const key_type &intruders) } void -LocalProcessorCellContexts::compute_results (db::Cell *cell, LocalProcessor *proc) +LocalProcessorCellContexts::compute_results (LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, LocalProcessor *proc) { bool first = true; std::set common; @@ -213,13 +213,13 @@ LocalProcessorCellContexts::compute_results (db::Cell *cell, LocalProcessor *pro if (first) { common = c->second.propagated (); - proc->compute_local_cell (cell, c->first, common); + proc->compute_local_cell (contexts, cell, op, c->first, common); first = false; } else { std::set res = c->second.propagated (); - proc->compute_local_cell (cell, c->first, res); + proc->compute_local_cell (contexts, cell, op, c->first, res); if (common.empty ()) { @@ -252,7 +252,7 @@ LocalProcessorCellContexts::compute_results (db::Cell *cell, LocalProcessor *pro } - proc->push_results (cell, common); + proc->push_results (cell, output_layer, common); } // --------------------------------------------------------------------------------------------- @@ -476,36 +476,40 @@ private: // --------------------------------------------------------------------------------------------- // LocalProcessor implementation -LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer) - : mp_layout (layout), mp_top (top), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_output_layer (output_layer), mp_op (op) +LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top) + : mp_layout (layout), mp_top (top) { - set_description (op->description ()); + // .. nothing yet .. } -void LocalProcessor::run () +void LocalProcessor::run (LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer, db::Coord dist) { - compute_contexts (); - compute_results (); + LocalProcessorContexts contexts; + compute_contexts (contexts, op, subject_layer, intruder_layer, dist); + compute_results (contexts, op, output_layer); } -void LocalProcessor::push_results (db::Cell *cell, const std::set &result) +void LocalProcessor::push_results (db::Cell *cell, unsigned int output_layer, const std::set &result) const { if (! result.empty ()) { - cell->shapes (m_output_layer).insert (result.begin (), result.end ()); + cell->shapes (output_layer).insert (result.begin (), result.end ()); } } -void LocalProcessor::compute_contexts () +void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, db::Coord dist) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing contexts for ")) + description ()); - m_contexts_per_cell.clear (); + contexts.clear (); + contexts.set_intruder_layer (intruder_layer); + contexts.set_subject_layer (subject_layer); + contexts.set_description (op->description ()); std::pair, std::set > intruders; - compute_contexts (0, 0, mp_top, db::ICplxTrans (), intruders); + compute_contexts (contexts, 0, 0, mp_top, db::ICplxTrans (), dist, intruders); } -void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders) +void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, db::Coord dist, const std::pair, std::set > &intruders) { if (tl::verbosity () >= 30) { if (! parent) { @@ -515,22 +519,22 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con } } - db::LocalProcessorCellContexts &contexts = m_contexts_per_cell [cell]; + db::LocalProcessorCellContexts &cell_contexts = contexts.contexts_per_cell (cell); - db::LocalProcessorCellContext *context = contexts.find_context (intruders); + db::LocalProcessorCellContext *context = cell_contexts.find_context (intruders); if (context) { context->add (parent_context, parent, cell_inst); return; } - context = contexts.create (intruders); + context = cell_contexts.create (intruders); context->add (parent_context, parent, cell_inst); - const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); + const db::Shapes &shapes_intruders = cell->shapes (contexts.intruder_layer ()); - db::box_convert inst_bcs (*mp_layout, m_subject_layer); - db::box_convert inst_bci (*mp_layout, m_intruder_layer); - db::box_convert inst_bcii (*mp_layout, m_intruder_layer); + db::box_convert inst_bcs (*mp_layout, contexts.subject_layer ()); + db::box_convert inst_bci (*mp_layout, contexts.intruder_layer ()); + db::box_convert inst_bcii (*mp_layout, contexts.intruder_layer ()); if (! cell->begin ().at_end ()) { @@ -545,7 +549,7 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con { db::box_scanner2 scanner; - InteractionRegistrationInst2Inst rec (mp_layout, m_subject_layer, mp_layout, m_intruder_layer, &interactions); + InteractionRegistrationInst2Inst rec (mp_layout, contexts.subject_layer (), mp_layout, contexts.intruder_layer (), &interactions); for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { if (! inst_bcs (i->cell_inst ()).empty ()) { @@ -567,7 +571,7 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con { db::box_scanner2 scanner; - InteractionRegistrationInst2Shape rec (mp_layout, m_subject_layer, &interactions); + InteractionRegistrationInst2Shape rec (mp_layout, contexts.subject_layer (), &interactions); for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { if (! inst_bcs (i->cell_inst ()).empty ()) { @@ -593,7 +597,7 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con db::ICplxTrans tn = i->first->complex_trans (*n); db::ICplxTrans tni = tn.inverted (); - db::Box nbox = tn * child_cell.bbox (m_subject_layer); + db::Box nbox = tn * child_cell.bbox (contexts.subject_layer ()); if (! nbox.empty ()) { @@ -615,7 +619,7 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con } } - compute_contexts (context, cell, &child_cell, tn, intruders_below); + compute_contexts (contexts, context, cell, &child_cell, tn, dist, intruders_below); } @@ -627,7 +631,7 @@ void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_con } void -LocalProcessor::compute_results () +LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int output_layer) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing results for ")) + description ()); @@ -637,27 +641,27 @@ LocalProcessor::compute_results () for (db::Layout::bottom_up_const_iterator bu = mp_layout->begin_bottom_up (); bu != mp_layout->end_bottom_up (); ++bu) { - contexts_per_cell_type::iterator cpc = m_contexts_per_cell.find (&mp_layout->cell (*bu)); - if (cpc != m_contexts_per_cell.end ()) { - cpc->second.compute_results (cpc->first, this); - m_contexts_per_cell.erase (cpc); + LocalProcessorContexts::iterator cpc = contexts.context_map ().find (&mp_layout->cell (*bu)); + if (cpc != contexts.context_map ().end ()) { + cpc->second.compute_results (contexts, cpc->first, op, output_layer, this); + contexts.context_map ().erase (cpc); } } } void -LocalProcessor::compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const +LocalProcessor::compute_local_cell (LocalProcessorContexts &contexts, db::Cell *cell, const db::LocalOperation *op, const std::pair, std::set > &intruders, std::set &result) { - const db::Shapes &shapes_subject = cell->shapes (m_subject_layer); - const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); + const db::Shapes &shapes_subject = cell->shapes (contexts.subject_layer ()); + const db::Shapes &shapes_intruders = cell->shapes (contexts.intruder_layer ()); // local shapes vs. child cell std::map > interactions; - db::box_convert inst_bci (*mp_layout, m_intruder_layer); + db::box_convert inst_bci (*mp_layout, contexts.intruder_layer ()); - if (mp_op->on_empty_intruder_hint () != LocalOperation::Drop) { + if (op->on_empty_intruder_hint () != LocalOperation::Drop) { // insert dummy interactions to accommodate subject vs. nothing for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); @@ -687,7 +691,7 @@ LocalProcessor::compute_local_cell (db::Cell *cell, const std::pairbegin ().at_end () && intruders.first.empty ())) { db::box_scanner2 scanner; - InteractionRegistrationShape2Inst rec (mp_layout, m_intruder_layer, &interactions); + InteractionRegistrationShape2Inst rec (mp_layout, contexts.intruder_layer (), &interactions); for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); @@ -708,7 +712,7 @@ LocalProcessor::compute_local_cell (db::Cell *cell, const std::paircompute_local (mp_layout, interactions, result); + op->compute_local (mp_layout, interactions, result); } } diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index 47d1e2478..6113e50d7 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -37,6 +37,7 @@ namespace db class LocalProcessor; class LocalProcessorCellContext; +class LocalProcessorContexts; class DB_PLUGIN_PUBLIC LocalOperation { @@ -118,7 +119,7 @@ public: db::LocalProcessorCellContext *find_context (const key_type &intruders); db::LocalProcessorCellContext *create (const key_type &intruders); - void compute_results (db::Cell *cell, LocalProcessor *proc); + void compute_results (LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, LocalProcessor *proc); iterator begin () const { @@ -134,21 +135,87 @@ private: std::map m_contexts; }; -class DB_PLUGIN_PUBLIC LocalProcessor +class DB_PLUGIN_PUBLIC LocalProcessorContexts { public: typedef std::map contexts_per_cell_type; + typedef contexts_per_cell_type::iterator iterator; - LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer); - void run (); - void compute_contexts (); - void compute_results (); + LocalProcessorContexts () + : m_subject_layer (0), m_intruder_layer (0) + { + // .. nothing yet .. + } - const contexts_per_cell_type &contexts_per_cell () const + void clear () + { + m_contexts_per_cell.clear (); + } + + LocalProcessorCellContexts &contexts_per_cell (db::Cell *cell) + { + return m_contexts_per_cell [cell]; + } + + contexts_per_cell_type &context_map () { return m_contexts_per_cell; } + iterator begin () + { + return m_contexts_per_cell.begin (); + } + + iterator end () + { + return m_contexts_per_cell.end (); + } + + void set_subject_layer (unsigned int l) + { + m_subject_layer = l; + } + + unsigned int subject_layer () const + { + return m_subject_layer; + } + + void set_intruder_layer (unsigned int l) + { + m_intruder_layer = l; + } + + unsigned int intruder_layer () const + { + return m_intruder_layer; + } + + void set_description (const std::string &desc) + { + m_description = desc; + } + + const std::string &description () const + { + return m_description; + } + +private: + contexts_per_cell_type m_contexts_per_cell; + unsigned int m_subject_layer, m_intruder_layer; + std::string m_description; +}; + +class DB_PLUGIN_PUBLIC LocalProcessor +{ +public: + LocalProcessor (db::Layout *layout, db::Cell *top); + void run (LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer, db::Coord dist = 0); + void compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, db::Coord dist = 0); + void compute_results (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int output_layer); + void set_description (const std::string &d) { m_description = d; @@ -164,14 +231,11 @@ private: db::Layout *mp_layout; db::Cell *mp_top; - unsigned int m_subject_layer, m_intruder_layer, m_output_layer; - contexts_per_cell_type m_contexts_per_cell; - LocalOperation *mp_op; std::string m_description; - void compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders); - void push_results (db::Cell *cell, const std::set &result); - void compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const; + void compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, db::Coord dist, const std::pair, std::set > &intruders); + void push_results (db::Cell *cell, unsigned int output_layer, const std::set &result) const; + void compute_local_cell (LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, const std::pair, std::set > &intruders, std::set &result); }; } diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc index ea5357edf..3818caae6 100644 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc @@ -153,8 +153,8 @@ NetExtractor::and_or_not (NetLayer a, NetLayer b, bool is_and) unsigned int lout = mp_layout->insert_layer (); db::BoolAndOrNotLocalOperation op (is_and); - db::LocalProcessor proc (mp_layout, mp_top_cell, &op, a.layer_index (), b.layer_index (), lout); - proc.run (); + db::LocalProcessor proc (mp_layout, mp_top_cell); + proc.run (&op, a.layer_index (), b.layer_index (), lout); return NetLayer (lout); } diff --git a/src/plugins/tools/netx/testdata/hlp10.oas b/src/plugins/tools/netx/testdata/hlp10.oas index 6ca1d9c163b2000b74dc80fb15ebd1463cbe0f3f..707800ad0bd66ef42588996501cbcf3fc123a0a7 100644 GIT binary patch delta 112 zcmcb|{E~S>F)ypLho_GVGjoW4058MD2B`qe6h@{F^$%^f6CeI&X=q?z F000|UA=&@{ delta 102 zcmaFKe2;lTF)wq7e*iP9vxldT3oqlu1}Rs|R7R!_^%HCdco;+tQ$XwwY!_I083cba zZg|GBVj?3G;|DfT`&0&q)Q3+j3$nNwKClUh8h}KOaPE1;!Zh*Yw@HlbObiSF{dFFa diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index f035a547c..afb862b97 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -57,13 +57,13 @@ void normalize_layer (db::Layout &layout, unsigned int layer) } -std::string contexts_to_s (db::Layout *layout, const db::LocalProcessor::contexts_per_cell_type &contexts) +std::string contexts_to_s (db::Layout *layout, db::LocalProcessorContexts &contexts) { std::string res; for (db::Layout::top_down_const_iterator i = layout->begin_top_down (); i != layout->end_top_down(); ++i) { - db::LocalProcessor::contexts_per_cell_type::const_iterator cc = contexts.find (&layout->cell (*i)); - if (cc != contexts.end ()) { + db::LocalProcessorContexts::iterator cc = contexts.context_map ().find (&layout->cell (*i)); + if (cc != contexts.context_map ().end ()) { int index = 1; for (db::LocalProcessorCellContexts::iterator j = cc->second.begin (); j != cc->second.end (); ++j) { res += tl::sprintf ("%s[%d] %d insts, %d shapes (%d times)\n", layout->cell_name (*i), index, int (j->first.first.size ()), int (j->first.second.size ()), int (j->second.size ())); @@ -75,7 +75,7 @@ std::string contexts_to_s (db::Layout *layout, const db::LocalProcessor::context return res; } -void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0) +void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, db::Coord enl = 0, std::string *context_doc = 0) { db::Layout layout_org; @@ -114,14 +114,15 @@ void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int ou normalize_layer (layout_org, l2); db::BoolAndOrNotLocalOperation op (mode == TMAnd); - db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &op, l1, l2, lout); + db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); if (! context_doc) { - proc.run (); + proc.run (&op, l1, l2, lout); } else { - proc.compute_contexts (); - *context_doc = contexts_to_s (&layout_org, proc.contexts_per_cell ()); - proc.compute_results (); + db::LocalProcessorContexts contexts; + proc.compute_contexts (contexts, &op, l1, l2, enl); + *context_doc = contexts_to_s (&layout_org, contexts); + proc.compute_results (contexts, &op, lout); } db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); @@ -227,7 +228,7 @@ TEST(BasicAnd9) { // Top-level ring structure, AND std::string doc; - run_test_bool (_this, "hlp9.oas", TMAnd, 100, &doc); + run_test_bool (_this, "hlp9.oas", TMAnd, 100, 0, &doc); EXPECT_EQ (doc, // This means: the interaction test is strong enough, so it does not see interactions between the // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes @@ -243,7 +244,7 @@ TEST(BasicNot9) { // Top-level ring structure, NOT std::string doc; - run_test_bool (_this, "hlp9.oas", TMNot, 101, &doc); + run_test_bool (_this, "hlp9.oas", TMNot, 101, 0, &doc); EXPECT_EQ (doc, // This means: the interaction test is strong enough, so it does not see interactions between the // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes From defbf33d197daafbf38d27701e4b99ca22b3aede Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Sep 2018 23:26:18 +0200 Subject: [PATCH 017/335] WIP: distance parameter for hierarchical processor. --- .../tools/netx/db_plugin/dbHierProcessor.cc | 67 ++--- .../tools/netx/db_plugin/dbHierProcessor.h | 7 +- src/plugins/tools/netx/testdata/hlp1.oas | Bin 583 -> 691 bytes src/plugins/tools/netx/testdata/hlp10.oas | Bin 489 -> 560 bytes src/plugins/tools/netx/testdata/hlp2.oas | Bin 827 -> 1039 bytes src/plugins/tools/netx/testdata/hlp3.oas | Bin 649 -> 755 bytes src/plugins/tools/netx/testdata/hlp4.oas | Bin 667 -> 768 bytes src/plugins/tools/netx/testdata/hlp5.oas | Bin 763 -> 868 bytes src/plugins/tools/netx/testdata/hlp6.oas | Bin 969 -> 1298 bytes src/plugins/tools/netx/testdata/hlp7.oas | Bin 693 -> 795 bytes src/plugins/tools/netx/testdata/hlp8.oas | Bin 815 -> 955 bytes src/plugins/tools/netx/testdata/hlp9.oas | Bin 724 -> 845 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 236 +++++++++++++++++- 13 files changed, 272 insertions(+), 38 deletions(-) diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index b5e25f8b6..47ab20388 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -289,8 +289,8 @@ struct InteractionRegistrationShape2Inst : db::box_scanner_receiver2 { public: - InteractionRegistrationShape2Inst (db::Layout *layout, unsigned int intruder_layer, std::map > *result) - : mp_layout (layout), m_intruder_layer (intruder_layer), mp_result (result) + InteractionRegistrationShape2Inst (db::Layout *layout, unsigned int intruder_layer, db::Coord dist, std::map > *result) + : mp_layout (layout), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result) { // nothing yet .. } @@ -300,11 +300,11 @@ public: const db::Cell &intruder_cell = mp_layout->cell (inst->object ().cell_index ()); db::box_convert inst_bc (*mp_layout, m_intruder_layer); - for (db::CellInstArray::iterator n = inst->begin_touching (ref->box ().enlarged (db::Vector (-1, -1)), inst_bc); !n.at_end (); ++n) { + for (db::CellInstArray::iterator n = inst->begin_touching (ref->box ().enlarged (db::Vector (m_dist - 1, m_dist - 1)), inst_bc); !n.at_end (); ++n) { db::ICplxTrans tn = inst->complex_trans (*n); - db::Box region = ref->box ().transformed (tn.inverted ()) & intruder_cell.bbox (m_intruder_layer); + db::Box region = ref->box ().transformed (tn.inverted ()).enlarged (db::Vector (m_dist, m_dist)) & intruder_cell.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist)); if (! region.empty ()) { // @@@ TODO: should be lighter, cache, handle arrays .. @@ -312,7 +312,7 @@ public: si.shape_flags (polygon_ref_flags ()); while (! si.at_end ()) { - // @@@ should be easier to transform references + // @@@ it should be easier to transform references const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); db::Polygon poly = ref2->obj ().transformed (tn * si.trans () * db::ICplxTrans (ref2->trans ())); (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_layout->shape_repository())); @@ -329,11 +329,12 @@ public: private: db::Layout *mp_layout; unsigned int m_intruder_layer; + db::Coord m_dist; std::map > *mp_result; }; static bool -instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, unsigned int layer1, const db::Layout *layout2, const db::CellInstArray *inst2, unsigned int layer2) +instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, unsigned int layer1, const db::Layout *layout2, const db::CellInstArray *inst2, unsigned int layer2, db::Coord dist) { // TODO: this algorithm is not in particular effective for identical arrays @@ -347,7 +348,7 @@ instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, u db::ICplxTrans tn1 = inst1->complex_trans (*n); db::ICplxTrans tni1 = tn1.inverted (); - db::Box ibox1 = tn1 * cell1.bbox (layer1); + db::Box ibox1 = tn1 * cell1.bbox (layer1).enlarged (db::Vector (dist, dist)); if (! ibox1.empty ()) { @@ -361,7 +362,9 @@ instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, u } db::ICplxTrans tn2 = inst2->complex_trans (*k); - db::Box ibox2 = tn2 * cell2.bbox (layer2); + + // NOTE: we need to enlarge both subject *and* intruder boxes - either ubject comes close to intruder or the other way around + db::Box ibox2 = tn2 * cell2.bbox (layer2).enlarged (db::Vector (dist, dist)); db::ICplxTrans tn21 = tni1 * tn2; if (! relative_trans_seen.insert (tn21).second) { @@ -396,8 +399,8 @@ struct InteractionRegistrationInst2Inst : db::box_scanner_receiver2 { public: - InteractionRegistrationInst2Inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, std::map, std::set > > *result) - : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), mp_result (result) + InteractionRegistrationInst2Inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, db::Coord dist, std::map, std::set > > *result) + : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result) { // nothing yet .. } @@ -408,7 +411,7 @@ public: // NOTE: self-interactions are possible for arrays: different elements of the // array may interact which is a cell-external interaction. if ((*inst1 != *inst2 || inst1->size () > 1) - && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer)) { + && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer, m_dist)) { (*mp_result) [inst1].first.insert (inst2); } } @@ -416,20 +419,21 @@ public: private: const db::Layout *mp_subject_layout, *mp_intruder_layout; unsigned int m_subject_layer, m_intruder_layer; + db::Coord m_dist; std::map, std::set > > *mp_result; }; static bool -instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *inst, unsigned int layer, const db::PolygonRef &ref) +instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *inst, unsigned int layer, const db::PolygonRef &ref, db::Coord dist) { const db::Cell &cell = layout->cell (inst->object ().cell_index ()); db::box_convert inst_bc (*layout, layer); db::Box rbox = ref.box (); - for (db::CellInstArray::iterator n = inst->begin_touching (rbox.enlarged (db::Vector (-1, -1)), inst_bc); ! n.at_end (); ++n) { + for (db::CellInstArray::iterator n = inst->begin_touching (rbox.enlarged (db::Vector (dist - 1, dist - 1)), inst_bc); ! n.at_end (); ++n) { db::ICplxTrans tn = inst->complex_trans (*n); - db::Box cbox = (tn * cell.bbox (layer)) & rbox; + db::Box cbox = (tn * cell.bbox (layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist)); if (! cbox.empty ()) { @@ -452,15 +456,15 @@ struct InteractionRegistrationInst2Shape : db::box_scanner_receiver2 { public: - InteractionRegistrationInst2Shape (const db::Layout *subject_layout, unsigned int subject_layer, std::map, std::set > > *result) - : mp_subject_layout (subject_layout), m_subject_layer (subject_layer), mp_result (result) + InteractionRegistrationInst2Shape (const db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, std::map, std::set > > *result) + : mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result) { // nothing yet .. } void add (const db::CellInstArray *inst, int, const db::PolygonRef *ref, int) { - if (instance_shape_interacts (mp_subject_layout, inst, m_subject_layer, *ref)) { + if (instance_shape_interacts (mp_subject_layout, inst, m_subject_layer, *ref, m_dist)) { (*mp_result) [inst].second.insert (*ref); } } @@ -468,6 +472,7 @@ public: private: const db::Layout *mp_subject_layout; unsigned int m_subject_layer; + db::Coord m_dist; std::map, std::set > > *mp_result; }; @@ -482,10 +487,10 @@ LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top) // .. nothing yet .. } -void LocalProcessor::run (LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer, db::Coord dist) +void LocalProcessor::run (LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer) { LocalProcessorContexts contexts; - compute_contexts (contexts, op, subject_layer, intruder_layer, dist); + compute_contexts (contexts, op, subject_layer, intruder_layer); compute_results (contexts, op, output_layer); } @@ -496,7 +501,7 @@ void LocalProcessor::push_results (db::Cell *cell, unsigned int output_layer, co } } -void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, db::Coord dist) +void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing contexts for ")) + description ()); @@ -506,10 +511,10 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const L contexts.set_description (op->description ()); std::pair, std::set > intruders; - compute_contexts (contexts, 0, 0, mp_top, db::ICplxTrans (), dist, intruders); + compute_contexts (contexts, 0, 0, mp_top, db::ICplxTrans (), intruders, op->dist ()); } -void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, db::Coord dist, const std::pair, std::set > &intruders) +void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders, db::Coord dist) { if (tl::verbosity () >= 30) { if (! parent) { @@ -549,7 +554,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Loc { db::box_scanner2 scanner; - InteractionRegistrationInst2Inst rec (mp_layout, contexts.subject_layer (), mp_layout, contexts.intruder_layer (), &interactions); + InteractionRegistrationInst2Inst rec (mp_layout, contexts.subject_layer (), mp_layout, contexts.intruder_layer (), dist, &interactions); for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { if (! inst_bcs (i->cell_inst ()).empty ()) { @@ -566,12 +571,12 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Loc } } - scanner.process (rec, 0, inst_bcs, inst_bci); + scanner.process (rec, dist, inst_bcs, inst_bci); } { db::box_scanner2 scanner; - InteractionRegistrationInst2Shape rec (mp_layout, contexts.subject_layer (), &interactions); + InteractionRegistrationInst2Shape rec (mp_layout, contexts.subject_layer (), dist, &interactions); for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { if (! inst_bcs (i->cell_inst ()).empty ()) { @@ -586,7 +591,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Loc scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); } - scanner.process (rec, 0, inst_bcs, db::box_convert ()); + scanner.process (rec, dist, inst_bcs, db::box_convert ()); } for (std::map, std::set > >::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { @@ -597,7 +602,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Loc db::ICplxTrans tn = i->first->complex_trans (*n); db::ICplxTrans tni = tn.inverted (); - db::Box nbox = tn * child_cell.bbox (contexts.subject_layer ()); + db::Box nbox = tn * child_cell.bbox (contexts.subject_layer ()).enlarged (db::Vector (dist, dist)); if (! nbox.empty ()) { @@ -619,7 +624,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Loc } } - compute_contexts (contexts, context, cell, &child_cell, tn, dist, intruders_below); + compute_contexts (contexts, context, cell, &child_cell, tn, intruders_below, dist); } @@ -684,14 +689,14 @@ LocalProcessor::compute_local_cell (LocalProcessorContexts &contexts, db::Cell * scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); } - scanner.process (rec, 0, db::box_convert (), db::box_convert ()); + scanner.process (rec, op->dist (), db::box_convert (), db::box_convert ()); } if (! shapes_subject.empty () && ! (cell->begin ().at_end () && intruders.first.empty ())) { db::box_scanner2 scanner; - InteractionRegistrationShape2Inst rec (mp_layout, contexts.intruder_layer (), &interactions); + InteractionRegistrationShape2Inst rec (mp_layout, contexts.intruder_layer (), op->dist (), &interactions); for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); @@ -708,7 +713,7 @@ LocalProcessor::compute_local_cell (LocalProcessorContexts &contexts, db::Cell * } } - scanner.process (rec, 0, db::box_convert (), inst_bci); + scanner.process (rec, op->dist (), db::box_convert (), inst_bci); } diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index 6113e50d7..56c7a0b20 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -52,6 +52,7 @@ public: virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const = 0; virtual on_empty_intruder_mode on_empty_intruder_hint () const = 0; virtual std::string description () const = 0; + virtual db::Coord dist () const { return 0; } }; class DB_PLUGIN_PUBLIC BoolAndOrNotLocalOperation @@ -212,8 +213,8 @@ class DB_PLUGIN_PUBLIC LocalProcessor { public: LocalProcessor (db::Layout *layout, db::Cell *top); - void run (LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer, db::Coord dist = 0); - void compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, db::Coord dist = 0); + void run (LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer); + void compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer); void compute_results (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int output_layer); void set_description (const std::string &d) @@ -233,7 +234,7 @@ private: db::Cell *mp_top; std::string m_description; - void compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, db::Coord dist, const std::pair, std::set > &intruders); + void compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders, db::Coord dist); void push_results (db::Cell *cell, unsigned int output_layer, const std::set &result) const; void compute_local_cell (LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, const std::pair, std::set > &intruders, std::set &result); }; diff --git a/src/plugins/tools/netx/testdata/hlp1.oas b/src/plugins/tools/netx/testdata/hlp1.oas index 17754711a3ba196f11516e58c629422d70438e83..7dee89a076c77d0e72e4a3c29d851723d02402dc 100644 GIT binary patch delta 128 zcmX@kvYB;*J7cb>Wf~*f2BitiKe-mj|KQrd*>IHUgPy1bBhv%z4|*^DG3^isaV}`Q zV1H0{LsrxP#9pEMV<#h!$?`+FgYO2AoFF4=m=2OTpn9SXr2Ym=2j2vl3yvW651`2t VPB5)-W11Yu_+@eeBPSCB0|0ATZx*AC%pY tg}L>|P87FJkbyh)L>KPCq-u`)3*007s{YS{n) delta 89 zcmeC@*v&SfNQ#9y#6N(6h0WQ+)5pb-5y~)P=4GDT&S*8UV~Q9rgWw0o2{JpTFbaNP peshiSf`&*nBg15VCS7I`OQy*;7)>VIFr_mxO%`PS!`#5Y000P#7gYcN diff --git a/src/plugins/tools/netx/testdata/hlp3.oas b/src/plugins/tools/netx/testdata/hlp3.oas index 765d95764f3cf57d0d0dfe60aa849bf9cf510673..10ecef81b160e9b52e01652290c367dbf9e923f3 100644 GIT binary patch delta 185 zcmeBV{meR{n2*ib!_&vbkeNBeKY*8EVq;CcsAU=>+Xkfx%s;sn$p7Hl!1-YX(++V_ z3r3~~+8^|8JYr%1u`g)6V80NxfKAi@#9pEMVlE?)$?`+FgYO2AWDqq>2T2@I-H`-R zeuJfhk70%ui1`C(Y{MF+4|=?ef}4FD1N0gxQH7YhKuJ`xmn0szz& h&?vBj0+YA_I|CItWs`UTH30&X0Rr%o5CRPX0015G6YT&1 diff --git a/src/plugins/tools/netx/testdata/hlp4.oas b/src/plugins/tools/netx/testdata/hlp4.oas index 0e43780b40094afce9ccba2545d64f650b3e6f22..9da959541d776048b6bf490829585188091ca774 100644 GIT binary patch delta 186 zcmbQu+Q2rUNR*k)*~8Pv#Ry6pGBbzx2TWXFS1)Rq&d78@>EL6Sp#@_8 z;94Nxu!iY_o~UISBijb03Cur%B0sn`aDG_9v_l-ED{V>u6#;0J~Y PG7Dw`X~xa-8Dkg$?4~w< delta 48 zcmaFD_M3G=H7|3Be*iOkaHx~BtB=pbjyfh@rpbkj-8_tf9~dUcESSk8_8;y)NWEn(F7@00;tkC^%gb~DM`JvpwcLPMK zGl-g|gX9jV9_R$h0nNC<(!tlEe!&sU`2n<|<1*6O>t#s|CbW6J&NA26I5(eDRNIM>h*dYJ<`PZJ>o87J+#n t7f(3B1VkXuNP>L*pzOv(7H)D%N7Ozcby3;=a;c~1ZU delta 68 zcmV-K0K5N^3dskMdkq6rPf!B~Q&mDkMNCYQh%o^Ik-Fv;4FM7O0FWG@nF0#|^aGLt a;sFux0<$Xu6afJOlOY7}lQ0Ah0ssKRcoP!< diff --git a/src/plugins/tools/netx/testdata/hlp7.oas b/src/plugins/tools/netx/testdata/hlp7.oas index a167833a837033f9f79d6445be8fe47e52ef7042..a8c2a5649f187110296a98af298e5be80c4db14b 100644 GIT binary patch delta 214 zcmdnWI-6}mu_&vvf0%0!Gn=!Ar;m#vGjoW4059Xj1C#4TEz=m;HYiPC{>imK{s-3v z&JQb?c8H5wFfu*R{-Af`5fcN5eL>>|`-P|lY@!Ar_6pq>a~Xk5mLJL;d^dn3gQ#IT zNaBF%jwF!s8!R1s3^TMq%pX8w8`dy=(BowgDQ050B6dMzLo<^|L4#zED1%7B#5u}( vOoA^MJ}hG4W;kFcB>01Ig3JzaZl)LP8(8`o1vapVl*4s_Rcvl$G++b(0|P=L delta 93 zcmbQuwv}~4u_&9fho_H=Av1G`e*iP9vwxUt5HHi@Hb!$shKU`Mjd&Rae=tmt*&)u& x{DOT0OFyFk(*_p7A50$>u`qBm9Iz7-DQ9B1B6dMzLo*XMBaqLu`8A^fBLI{48MOca diff --git a/src/plugins/tools/netx/testdata/hlp8.oas b/src/plugins/tools/netx/testdata/hlp8.oas index 27ca4eb75fd2088524110bed946c4b7a336d0ba0..670bec10b96c52c1408917ee6d6a2eba04ee59a3 100644 GIT binary patch delta 300 zcmZ3_wwrxIu^g+jf0%0!Gn=!Ar;m#vGkb8Tle4Rj4>NO!e*iDz#FI6_qK4^=Ocyi` zsP0G-wP0lW!L>lXVGYvL%3$!i(SWkq%2HZq9nqFBkm NG}(~p4|4+p0{}F1TbKX< delta 164 zcmdnZzMgGDu^gMTho_H=Av1G`e*iP9vwxUt5HovlsFSm+j}I@?Qq(QPrbR#D>Q;OsQMy8)!ULqwQM0bcc9A)ALYo6@RB*(}!`5@De$rqSd GfjR*0r7U&; diff --git a/src/plugins/tools/netx/testdata/hlp9.oas b/src/plugins/tools/netx/testdata/hlp9.oas index 3baf603e09e750fea443d8d612e6402edb772029..c12644748b66b69ebad38ca37f68a1b1c604bb16 100644 GIT binary patch delta 220 zcmcb@dX{ZMH4jUWr=L4BbBKQcFXO~^Ic8DTw24dnCu=k6O#Eb7Z<)r(wn1qE^G~h? z@;|sXaDG_9v_o9ff|2Qg_6NNikC+%h>2uRx zq}qs);e|qjB}4O1AI8jKdmzxd58YMKgS&kzJkaWl-35D-;N0|{Jk+&qQR Gfe`?-fktou delta 99 zcmX@hc7=69H4k%$e*iN}kf)zJFVn(r?74wERMp4yNMy3X%8G_slb0h>r4O2k81@bR`GjHZ)a$p1i D7$P2o diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index afb862b97..7034f412f 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -39,6 +39,41 @@ enum TestMode TMNot = 1 }; +/** + * @brief A new processor class which and/nots with a sized version of the intruder + */ +class BoolAndOrNotWithSizedLocalOperation + : public db::BoolAndOrNotLocalOperation +{ +public: + BoolAndOrNotWithSizedLocalOperation (bool is_and, db::Coord dist) + : BoolAndOrNotLocalOperation (is_and), m_dist (dist) + { + // .. nothing yet .. + } + + virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const + { + std::map > sized_interactions = interactions; + for (std::map >::iterator i = sized_interactions.begin (); i != sized_interactions.end (); ++i) { + for (std::vector::iterator j = i->second.begin (); j != i->second.end (); ++j) { + db::Polygon poly = j->obj ().transformed (j->trans ()); + poly.size (m_dist, m_dist); + *j = db::PolygonRef (poly, layout->shape_repository ()); + } + } + BoolAndOrNotLocalOperation::compute_local (layout, sized_interactions, result); + } + + db::Coord dist () const + { + return m_dist; + } + +private: + db::Coord m_dist; +}; + /** * @brief Turns a layer into polygons and polygon references * The hierarchical processor needs polygon references and can't work on polygons directly. @@ -75,7 +110,7 @@ std::string contexts_to_s (db::Layout *layout, db::LocalProcessorContexts &conte return res; } -void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, db::Coord enl = 0, std::string *context_doc = 0) +void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0) { db::Layout layout_org; @@ -120,7 +155,60 @@ void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int ou proc.run (&op, l1, l2, lout); } else { db::LocalProcessorContexts contexts; - proc.compute_contexts (contexts, &op, l1, l2, enl); + proc.compute_contexts (contexts, &op, l1, l2); + *context_doc = contexts_to_s (&layout_org, contexts); + proc.compute_results (contexts, &op, lout); + } + + db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); +} + +void run_test_bool_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0) +{ + db::Layout layout_org; + + unsigned int l1 = 0, l2 = 0, lout = 0; + db::LayerMap lmap; + + { + tl::InputStream stream (testdata (file)); + db::Reader reader (stream); + + db::LayerProperties p; + + p.layer = 1; + p.datatype = 0; + lmap.map (db::LDPair (1, 0), l1 = layout_org.insert_layer ()); + layout_org.set_properties (l1, p); + + p.layer = 2; + p.datatype = 0; + lmap.map (db::LDPair (2, 0), l2 = layout_org.insert_layer ()); + layout_org.set_properties (l2, p); + + p.layer = out_layer_num; + p.datatype = 0; + lmap.map (db::LDPair (out_layer_num, 0), lout = layout_org.insert_layer ()); + layout_org.set_properties (lout, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (layout_org, options); + } + + layout_org.clear_layer (lout); + normalize_layer (layout_org, l1); + normalize_layer (layout_org, l2); + + BoolAndOrNotWithSizedLocalOperation op (mode == TMAnd, dist); + db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); + + if (! context_doc) { + proc.run (&op, l1, l2, lout); + } else { + db::LocalProcessorContexts contexts; + proc.compute_contexts (contexts, &op, l1, l2); *context_doc = contexts_to_s (&layout_org, contexts); proc.compute_results (contexts, &op, lout); } @@ -228,7 +316,7 @@ TEST(BasicAnd9) { // Top-level ring structure, AND std::string doc; - run_test_bool (_this, "hlp9.oas", TMAnd, 100, 0, &doc); + run_test_bool (_this, "hlp9.oas", TMAnd, 100, &doc); EXPECT_EQ (doc, // This means: the interaction test is strong enough, so it does not see interactions between the // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes @@ -244,7 +332,7 @@ TEST(BasicNot9) { // Top-level ring structure, NOT std::string doc; - run_test_bool (_this, "hlp9.oas", TMNot, 101, 0, &doc); + run_test_bool (_this, "hlp9.oas", TMNot, 101, &doc); EXPECT_EQ (doc, // This means: the interaction test is strong enough, so it does not see interactions between the // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes @@ -268,3 +356,143 @@ TEST(BasicNot10) run_test_bool (_this, "hlp10.oas", TMNot, 101); } +TEST(BasicAndWithSize1) +{ + // Simple flat AND + run_test_bool_with_size (_this, "hlp1.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize1) +{ + // Simple flat NOT + run_test_bool_with_size (_this, "hlp1.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize2) +{ + // Up/down and down/up interactions, AND + run_test_bool_with_size (_this, "hlp2.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize2) +{ + // Up/down and down/up interactions, NOT + run_test_bool_with_size (_this, "hlp2.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize3) +{ + // Variant building, AND + run_test_bool_with_size (_this, "hlp3.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize3) +{ + // Variant building, NOT + run_test_bool_with_size (_this, "hlp3.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize4) +{ + // Sibling interactions, variant building, AND + run_test_bool_with_size (_this, "hlp4.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize4) +{ + // Sibling interactions, variant building, NOT + run_test_bool_with_size (_this, "hlp4.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize5) +{ + // Variant building with intermediate hierarchy, AND + run_test_bool_with_size (_this, "hlp5.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize5) +{ + // Variant building with intermediate hierarchy, NOT + run_test_bool_with_size (_this, "hlp5.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize6) +{ + // Extreme variants (copy, vanishing), AND + run_test_bool_with_size (_this, "hlp6.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize6) +{ + // Extreme variants (copy, vanishing), NOT + run_test_bool_with_size (_this, "hlp6.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize7) +{ + // Context replication - direct and indirect, AND + run_test_bool_with_size (_this, "hlp7.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize7) +{ + // Context replication - direct and indirect, NOT + run_test_bool_with_size (_this, "hlp7.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize8) +{ + // Mixed sibling-parent contexts, AND + run_test_bool_with_size (_this, "hlp8.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize8) +{ + // Mixed sibling-parent contexts, NOT + run_test_bool_with_size (_this, "hlp8.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize9) +{ + // Top-level ring structure, AND + std::string doc; + run_test_bool_with_size (_this, "hlp9.oas", TMAnd, 1500, 102, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + ); +} + +TEST(BasicNotWithSize9) +{ + // Top-level ring structure, NOT + std::string doc; + run_test_bool_with_size (_this, "hlp9.oas", TMNot, 1500, 103, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + ); +} + +TEST(BasicAndWithSize10) +{ + // Array instances, AND + run_test_bool_with_size (_this, "hlp10.oas", TMAnd, 150, 102); +} + +TEST(BasicNotWithSize10) +{ + // Array instances, NOT + run_test_bool_with_size (_this, "hlp10.oas", TMNot, 150, 103); +} + From ca6a46dabea79d87bd602822d657490c59e580e1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Sep 2018 22:19:02 +0200 Subject: [PATCH 018/335] COPYRIGHT file updated for Debian packager --- COPYRIGHT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COPYRIGHT b/COPYRIGHT index 4d49b0a1b..11c6389d8 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,6 +1,6 @@ klayout is packaged by Peter C.S. Scholtens and Matthias Köfferlein -and was obtained from https://www.klayout.org/downloads/source/klayout-0.25.4.tar.gz +and was obtained from https://www.klayout.org/downloads/source/klayout-0.25.5.tar.gz Authors: Matthias Köfferlein From ad7154c6cf0e8747d558f2e699d335d1ab37bc1a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Sep 2018 23:35:30 +0200 Subject: [PATCH 019/335] Basic performance improvement in the bitmap to image area Empty bitmaps are skipped now --- src/laybasic/laybasic/layBitmapsToImage.cc | 97 +++++++++++++--------- 1 file changed, 56 insertions(+), 41 deletions(-) diff --git a/src/laybasic/laybasic/layBitmapsToImage.cc b/src/laybasic/laybasic/layBitmapsToImage.cc index 2d8d7e3eb..07dc74fee 100644 --- a/src/laybasic/laybasic/layBitmapsToImage.cc +++ b/src/laybasic/laybasic/layBitmapsToImage.cc @@ -386,13 +386,18 @@ render_scanline_cross (const uint32_t *dp, unsigned int ds, const lay::Bitmap *p } } -static void create_precursor_bitmaps (const std::vector &view_ops_in, const std::vector &pbitmaps_in, const lay::LineStyles &ls, unsigned int width, unsigned int height, std::map &precursors, QMutex *mutex) +static void create_precursor_bitmaps (const std::vector &view_ops_in, const std::vector &vo_map, const std::vector &pbitmaps_in, const std::vector &bm_map, const lay::LineStyles &ls, unsigned int width, unsigned int height, std::map &precursors, QMutex *mutex) { + tl_assert (bm_map.size () == vo_map.size ()); + // Styled lines with width > 1 are not rendered directly, but through an intermediate step. // We prepare the necessary precursor bitmaps now - for (unsigned int i = 0; i < view_ops_in.size (); ++i) { + for (unsigned int i = 0; i < vo_map.size (); ++i) { - const ViewOp &op = view_ops_in [i]; + unsigned int vo_index = vo_map [i]; + unsigned int bm_index = bm_map [i]; + + const ViewOp &op = view_ops_in [vo_index]; if (op.width () > 1 && ls.style (op.line_style_index ()).width () > 0) { // lock bitmaps against change by the redraw thread @@ -400,12 +405,12 @@ static void create_precursor_bitmaps (const std::vector &view_ops_i mutex->lock (); } - lay::Bitmap &bp = precursors.insert (std::make_pair (i, lay::Bitmap (width, height, 1.0))).first->second; + lay::Bitmap &bp = precursors.insert (std::make_pair (bm_index, lay::Bitmap (width, height, 1.0))).first->second; LineStyleInfo ls_info = ls.style (op.line_style_index ()); ls_info.scale_pattern (op.width ()); for (unsigned int y = 0; y < height; y++) { - render_scanline_std_edge (ls_info.pattern (), ls_info.pattern_stride (), pbitmaps_in [i], y, width, height, bp.scanline (y)); + render_scanline_std_edge (ls_info.pattern (), ls_info.pattern_stride (), pbitmaps_in [bm_index], y, width, height, bp.scanline (y)); } if (mutex) { @@ -418,7 +423,7 @@ static void create_precursor_bitmaps (const std::vector &view_ops_i } static void -bitmaps_to_image_rgb (const std::vector &view_ops_in, +bitmaps_to_image_rgb (const std::vector &view_ops_in, const std::vector &pbitmaps_in, const lay::DitherPattern &dp, const lay::LineStyles &ls, @@ -427,29 +432,33 @@ bitmaps_to_image_rgb (const std::vector &view_ops_in, bool transparent, QMutex *mutex) { - unsigned int n_in = view_ops_in.size (); - std::vector bm_map; std::vector vo_map; - vo_map.reserve (n_in); - for (unsigned int i = 0; i < n_in; ++i) { - vo_map.push_back (i); - } + vo_map.reserve (view_ops_in.size ()); + bm_map.reserve (view_ops_in.size ()); + unsigned int n_in = 0; - if (! use_bitmap_index) { - bm_map = vo_map; - } else { - bm_map.reserve (n_in); - for (unsigned int i = 0; i < n_in; ++i) { - bm_map.push_back (view_ops_in [i].bitmap_index () < 0 ? i : view_ops_in [i].bitmap_index ()); + // drop invisible and empty bitmaps, build bitmap mask + for (unsigned int i = 0; i < view_ops_in.size (); ++i) { + + const lay::ViewOp &vop = view_ops_in [i]; + + unsigned int bi = (use_bitmap_index && vop.bitmap_index () >= 0) ? (unsigned int) vop.bitmap_index () : i; + const lay::Bitmap *pb = bi < pbitmaps_in.size () ? pbitmaps_in [bi] : 0; + + if ((vop.ormask () | ~vop.andmask ()) != 0 && pb && ! pb->empty ()) { + vo_map.push_back (i); + bm_map.push_back (bi); + ++n_in; } + } // Styled lines with width > 1 are not rendered directly, but through an intermediate step. // We prepare the necessary precursor bitmaps now std::map precursors; - create_precursor_bitmaps (view_ops_in, pbitmaps_in, ls, width, height, precursors, mutex); + create_precursor_bitmaps (view_ops_in, vo_map, pbitmaps_in, bm_map, ls, width, height, precursors, mutex); std::vector view_ops; std::vector pbitmaps; @@ -489,18 +498,19 @@ bitmaps_to_image_rgb (const std::vector &view_ops_in, unsigned int w = vop.width (); const lay::Bitmap *pb = 0; + unsigned int bm_index = bm_map[i]; if (bm_map [i] < pbitmaps_in.size ()) { if (w > 1 && ls.style (vop.line_style_index ()).width () > 0) { - tl_assert (precursors.find (i) != precursors.end ()); - pb = &precursors [i]; + tl_assert (precursors.find (bm_index) != precursors.end ()); + pb = &precursors [bm_index]; } else { - pb = pbitmaps_in [bm_map[i]]; + pb = pbitmaps_in [bm_index]; } } if (pb != 0 && w > 0 - && ((pb->first_scanline () < y + slice && pb->last_scanline () > y) || w > 1) + && (pb->first_scanline () + w - 1 < y + slice && pb->last_scanline () > y + w - 1) && (vop.ormask () | ~vop.andmask ()) != 0) { uint32_t non_empty_sl = 0; @@ -660,29 +670,33 @@ bitmaps_to_image_mono (const std::vector &view_ops_in, bool use_bitmap_index, QMutex *mutex) { - unsigned int n_in = view_ops_in.size (); - std::vector bm_map; std::vector vo_map; - vo_map.reserve (n_in); - for (unsigned int i = 0; i < n_in; ++i) { - vo_map.push_back (i); - } + vo_map.reserve (view_ops_in.size ()); + bm_map.reserve (view_ops_in.size ()); + unsigned int n_in = 0; - if (! use_bitmap_index) { - bm_map = vo_map; - } else { - bm_map.reserve (n_in); - for (unsigned int i = 0; i < n_in; ++i) { - bm_map.push_back (view_ops_in [i].bitmap_index () < 0 ? i : view_ops_in [i].bitmap_index ()); + // drop invisible and empty bitmaps, build bitmap mask + for (unsigned int i = 0; i < view_ops_in.size (); ++i) { + + const lay::ViewOp &vop = view_ops_in [i]; + + unsigned int bi = (use_bitmap_index && vop.bitmap_index () >= 0) ? (unsigned int) vop.bitmap_index () : i; + const lay::Bitmap *pb = bi < pbitmaps_in.size () ? pbitmaps_in [bi] : 0; + + if ((vop.ormask () | ~vop.andmask ()) != 0 && pb && ! pb->empty ()) { + vo_map.push_back (i); + bm_map.push_back (bi); + ++n_in; } + } // Styled lines with width > 1 are not rendered directly, but through an intermediate step. // We prepare the necessary precursor bitmaps now std::map precursors; - create_precursor_bitmaps (view_ops_in, pbitmaps_in, ls, width, height, precursors, mutex); + create_precursor_bitmaps (view_ops_in, vo_map, pbitmaps_in, bm_map, ls, width, height, precursors, mutex); std::vector view_ops; std::vector pbitmaps; @@ -722,18 +736,19 @@ bitmaps_to_image_mono (const std::vector &view_ops_in, unsigned int w = vop.width (); const lay::Bitmap *pb = 0; + unsigned int bm_index = bm_map[i]; if (bm_map [i] < pbitmaps_in.size ()) { if (w > 1 && ls.style (vop.line_style_index ()).width () > 0) { - tl_assert (precursors.find (i) != precursors.end ()); - pb = &precursors [i]; + tl_assert (precursors.find (bm_index) != precursors.end ()); + pb = &precursors [bm_index]; } else { - pb = pbitmaps_in [bm_map[i]]; + pb = pbitmaps_in [bm_index]; } } - if (pb != 0 + if (pb != 0 && w > 0 - && ((pb->first_scanline () < y + slice && pb->last_scanline () > y) || w > 1) + && (pb->first_scanline () + w - 1 < y + slice && pb->last_scanline () > y + w - 1) && (vop.ormask () | ~vop.andmask ()) != 0) { uint32_t non_empty_sl = 0; From c342bca584c26c5cf94f27002de9afaf3459a869 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Sep 2018 00:16:19 +0200 Subject: [PATCH 020/335] WIP: updated solution * Reverted first solution partially because it lead to drawing errors. * Redraw thread will fire workers only for layers that really need to be drawn --- src/laybasic/laybasic/layBitmapsToImage.cc | 4 ++-- src/laybasic/laybasic/layRedrawLayerInfo.h | 15 ++++++++++++--- src/laybasic/laybasic/layRedrawThread.cc | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/laybasic/laybasic/layBitmapsToImage.cc b/src/laybasic/laybasic/layBitmapsToImage.cc index 07dc74fee..67558c4e8 100644 --- a/src/laybasic/laybasic/layBitmapsToImage.cc +++ b/src/laybasic/laybasic/layBitmapsToImage.cc @@ -510,7 +510,7 @@ bitmaps_to_image_rgb (const std::vector &view_ops_in, if (pb != 0 && w > 0 - && (pb->first_scanline () + w - 1 < y + slice && pb->last_scanline () > y + w - 1) + && ((pb->first_scanline () < y + slice && pb->last_scanline () > y) || w > 1) && (vop.ormask () | ~vop.andmask ()) != 0) { uint32_t non_empty_sl = 0; @@ -748,7 +748,7 @@ bitmaps_to_image_mono (const std::vector &view_ops_in, if (pb != 0 && w > 0 - && (pb->first_scanline () + w - 1 < y + slice && pb->last_scanline () > y + w - 1) + && ((pb->first_scanline () < y + slice && pb->last_scanline () > y) || w > 1) && (vop.ormask () | ~vop.andmask ()) != 0) { uint32_t non_empty_sl = 0; diff --git a/src/laybasic/laybasic/layRedrawLayerInfo.h b/src/laybasic/laybasic/layRedrawLayerInfo.h index 3befe4c33..60865b241 100644 --- a/src/laybasic/laybasic/layRedrawLayerInfo.h +++ b/src/laybasic/laybasic/layRedrawLayerInfo.h @@ -74,9 +74,10 @@ struct RedrawLayerInfo /** * @brief The layer index * - * The logical layer to draw. If this member is <0 and the cellview_index is <0, it is an invalid layer, - * which is ignored. If the cellview_index is >=0, it denotes a "cell frame" pseudo - * layer. It is set by the constructor. + * The logical layer to draw. The layer index can be <0 which indicates a + * layer with not layout source (cell_frame may be true to indicate a + * pseudo layer then). + * This attribute is set by the constructor. */ int layer_index; @@ -115,6 +116,14 @@ struct RedrawLayerInfo * This member is set by the constructor. */ bool inverse_prop_sel; + + /** + * @brief Returns true, if the layer needs to be drawn + */ + bool needs_drawing () const + { + return visible && enabled && (cell_frame || layer_index >= 0) && cellview_index >= 0; + } }; } diff --git a/src/laybasic/laybasic/layRedrawThread.cc b/src/laybasic/laybasic/layRedrawThread.cc index ec2233de7..4ed200192 100644 --- a/src/laybasic/laybasic/layRedrawThread.cc +++ b/src/laybasic/laybasic/layRedrawThread.cc @@ -307,7 +307,7 @@ RedrawThread::do_start (bool clear, const db::Vector *shift_vector, const std::v } for (int i = 0; i < m_nlayers; ++i) { - if (m_layers [i].visible && m_layers [i].enabled) { + if (m_layers [i].needs_drawing ()) { schedule (new RedrawThreadTask (i)); } } From c08f0a1fdae400e4934b9cc5e5d21fd452d6f531 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Sep 2018 10:27:44 +0200 Subject: [PATCH 021/335] Fixed #176 by introducing a separate bitmap for text drawing optimization. --- .../laybasic/layRedrawThreadWorker.cc | 26 +++++++++++++------ src/laybasic/laybasic/layRedrawThreadWorker.h | 2 +- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index 93d8ce130..c0df55ba7 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -1232,20 +1232,26 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c vertex = m_planes[3 + plane_group * (planes_per_layer / 3)]; // do not draw, if there is nothing to draw - if (mp_layout->cells () <= ci || vp.empty ()) { + if (mp_layout->cells () <= ci || vp.empty () || mp_layout->cell (ci).bbox (m_layer).empty ()) { return; } if (cell_var_cached (ci, trans)) { return; } + std::auto_ptr opt_bitmap; + lay::Bitmap *vertex_bitmap = dynamic_cast (vertex); + if (m_text_lazy_rendering && vertex_bitmap) { + opt_bitmap.reset (new lay::Bitmap (vertex_bitmap->width (), vertex_bitmap->height (), vertex_bitmap->resolution ())); + } + for (std::vector::const_iterator b = vp.begin (); b != vp.end (); ++b) { - draw_text_layer (drawing_context, ci, trans, *b, level, fill, frame, vertex, text); + draw_text_layer (drawing_context, ci, trans, *b, level, fill, frame, vertex, text, opt_bitmap.get ()); } } void -RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, CanvasPlane *fill, CanvasPlane *frame, CanvasPlane *vertex, CanvasPlane *text) +RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, CanvasPlane *fill, CanvasPlane *frame, CanvasPlane *vertex, CanvasPlane *text, lay::Bitmap *opt_bitmap) { test_snapshot (0); @@ -1283,6 +1289,9 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c // paint the simplified box if (anything) { r.draw (trans * bbox, 0, frame, vertex, 0); + if (opt_bitmap) { + r.draw (trans * bbox, 0, 0, opt_bitmap, 0); + } } // do not dive further into hierarchy @@ -1318,6 +1327,9 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c test_snapshot (0); r.draw (*shape, trans, fill, frame, vertex, text); + if (opt_bitmap) { + r.draw (*shape, trans, 0, 0, opt_bitmap, 0); + } ++shape; --ntexts; @@ -1355,8 +1367,6 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c // dive down into the hierarchy .. if (need_to_dive) { - const lay::Bitmap *vertex_bitmap = dynamic_cast (vertex); - // create a set of boxes to look into std::vector vv = search_regions (cell_bbox, vp, level); @@ -1377,7 +1387,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c bool skip = false; if (m_text_lazy_rendering && qid != current_quad_id) { current_quad_id = qid; - skip = vertex_bitmap && skip_quad (inst.quad_box () & bbox, vertex_bitmap, trans); + skip = opt_bitmap && skip_quad (inst.quad_box () & bbox, opt_bitmap, trans); } if (skip) { @@ -1448,7 +1458,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c db::ICplxTrans t (cell_inst.complex_trans (*p)); db::Box new_vp = db::Box (t.inverted () * *v); - draw_text_layer (drawing_context, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text); + draw_text_layer (drawing_context, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text, opt_bitmap); } @@ -1648,7 +1658,7 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ // skip this quad if we have drawn something here already size_t qid = inst.quad_id (); bool skip = false; - if (m_text_lazy_rendering && qid != current_quad_id) { + if (qid != current_quad_id) { current_quad_id = qid; skip = skip_quad (inst.quad_box () & bbox, vertex_bitmap, trans); } diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.h b/src/laybasic/laybasic/layRedrawThreadWorker.h index 0b49a44d3..ea2a90a11 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.h +++ b/src/laybasic/laybasic/layRedrawThreadWorker.h @@ -180,7 +180,7 @@ private: void draw_layer (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot); void draw_layer_wo_cache (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vv, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot); void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); - void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text); + void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, Bitmap *opt_bitmap); void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level); void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); From 330dac8c74fc707cb81684cf48fce209e23539a6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Sep 2018 20:26:25 +0200 Subject: [PATCH 022/335] Updated Changelog. --- Changelog | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index df9613b49..3b7cbfe4a 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,4 @@ -0.25.5 (2018-09-26): +0.25.5 (2018-09-30): * Bugfix: https://github.com/klayoutmatthias/klayout/issues/162 GDS2 LIBNAME was not maintained on "File/Save". @@ -6,6 +6,10 @@ Internal error when writing GDS files (breaking of polygons) * Bugfix: https://github.com/klayoutmatthias/klayout/issues/172 DEF reader did not pull vias from LEF +* Bugfix: https://github.com/klayoutmatthias/klayout/issues/174 + Performance issue with many layers with width >1 +* Bugfix: https://github.com/klayoutmatthias/klayout/issues/175 + Painting issue with texts * Bugfix: some potential memory corruption issues fixed During the efforts for making the code base compatible with MSVC, some potential candidates for memory corruption From 28ddc02b18d1054909dbfbfd96b292036458854b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Sep 2018 23:04:09 +0200 Subject: [PATCH 023/335] Updated Debian changelog (for sync with next release) --- Changelog.Debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.Debian b/Changelog.Debian index 2075bc6af..01d8e502a 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -3,7 +3,7 @@ klayout (0.25.5-1) unstable; urgency=low * New features and bugfixes - See changelog - -- Matthias Köfferlein Wed, 19 Sep 2018 22:04:35 +0200 + -- Matthias Köfferlein Sun, 30 Sep 2018 23:02:44 +0200 klayout (0.25.4-1) unstable; urgency=low From 9dd603bbd55b0c6f6b9ad56b3f729466225b22f0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Sep 2018 23:27:25 +0200 Subject: [PATCH 024/335] Fixed a typo (issue number in changelog) --- Changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 3b7cbfe4a..67682c951 100644 --- a/Changelog +++ b/Changelog @@ -8,7 +8,7 @@ DEF reader did not pull vias from LEF * Bugfix: https://github.com/klayoutmatthias/klayout/issues/174 Performance issue with many layers with width >1 -* Bugfix: https://github.com/klayoutmatthias/klayout/issues/175 +* Bugfix: https://github.com/klayoutmatthias/klayout/issues/176 Painting issue with texts * Bugfix: some potential memory corruption issues fixed During the efforts for making the code base compatible From c380b9c4bf13c175124e7940956715c58bb38fd2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 1 Oct 2018 22:45:46 +0200 Subject: [PATCH 025/335] Hier processor: supports two layouts for input now. --- .../tools/netx/db_plugin/dbHierProcessor.cc | 199 ++++++---- .../tools/netx/db_plugin/dbHierProcessor.h | 21 +- .../netx/unit_tests/dbHierProcessorTests.cc | 371 +++++++++++++++++- 3 files changed, 503 insertions(+), 88 deletions(-) diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 47ab20388..94fb1435d 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -162,11 +162,11 @@ LocalProcessorCellContext::propagate (const std::set &res) tl_assert (d->parent_context != 0); tl_assert (d->parent != 0); - db::Layout *layout = d->parent->layout (); + db::Layout *subject_layout = d->parent->layout (); for (std::set::const_iterator r = res.begin (); r != res.end (); ++r) { db::Polygon poly = r->obj ().transformed (d->cell_inst * db::ICplxTrans (r->trans ())); - d->parent_context->propagated ().insert (db::PolygonRef (poly, layout->shape_repository ())); + d->parent_context->propagated ().insert (db::PolygonRef (poly, subject_layout->shape_repository ())); } } @@ -177,6 +177,13 @@ LocalProcessorCellContext::propagate (const std::set &res) // LocalProcessorCellContexts implementation LocalProcessorCellContexts::LocalProcessorCellContexts () + : mp_intruder_cell (0) +{ + // .. nothing yet .. +} + +LocalProcessorCellContexts::LocalProcessorCellContexts (const db::Cell *intruder_cell) + : mp_intruder_cell (intruder_cell) { // .. nothing yet .. } @@ -213,13 +220,13 @@ LocalProcessorCellContexts::compute_results (LocalProcessorContexts &contexts, d if (first) { common = c->second.propagated (); - proc->compute_local_cell (contexts, cell, op, c->first, common); + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, common); first = false; } else { std::set res = c->second.propagated (); - proc->compute_local_cell (contexts, cell, op, c->first, res); + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, res); if (common.empty ()) { @@ -270,35 +277,43 @@ struct InteractionRegistrationShape2Shape : db::box_scanner_receiver2 { public: - InteractionRegistrationShape2Shape (std::map > *result) - : mp_result (result) + InteractionRegistrationShape2Shape (db::Layout *layout, std::map > *result) + : mp_result (result), mp_layout (layout) { // nothing yet .. } void add (const db::PolygonRef *ref1, int, const db::PolygonRef *ref2, int) { - (*mp_result) [*ref1].push_back (*ref2); + if (mp_layout) { + // In order to guarantee the refs come from the subject layout, we'd need to + // rewrite them to the subject layout if required. + db::Polygon poly = ref2->obj ().transformed (ref2->trans ()); + (*mp_result) [*ref1].push_back (db::PolygonRef (poly, mp_layout->shape_repository ())); + } else { + (*mp_result) [*ref1].push_back (*ref2); + } } private: std::map > *mp_result; + db::Layout *mp_layout; }; struct InteractionRegistrationShape2Inst : db::box_scanner_receiver2 { public: - InteractionRegistrationShape2Inst (db::Layout *layout, unsigned int intruder_layer, db::Coord dist, std::map > *result) - : mp_layout (layout), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result) + InteractionRegistrationShape2Inst (db::Layout *subject_layout, const db::Layout *intruder_layout, unsigned int intruder_layer, db::Coord dist, std::map > *result) + : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result) { // nothing yet .. } void add (const db::PolygonRef *ref, int, const db::CellInstArray *inst, int) { - const db::Cell &intruder_cell = mp_layout->cell (inst->object ().cell_index ()); - db::box_convert inst_bc (*mp_layout, m_intruder_layer); + const db::Cell &intruder_cell = mp_intruder_layout->cell (inst->object ().cell_index ()); + db::box_convert inst_bc (*mp_intruder_layout, m_intruder_layer); for (db::CellInstArray::iterator n = inst->begin_touching (ref->box ().enlarged (db::Vector (m_dist - 1, m_dist - 1)), inst_bc); !n.at_end (); ++n) { @@ -308,14 +323,16 @@ public: if (! region.empty ()) { // @@@ TODO: should be lighter, cache, handle arrays .. - db::RecursiveShapeIterator si (*mp_layout, intruder_cell, m_intruder_layer, region); + db::RecursiveShapeIterator si (*mp_intruder_layout, intruder_cell, m_intruder_layer, region); si.shape_flags (polygon_ref_flags ()); while (! si.at_end ()) { // @@@ it should be easier to transform references const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); db::Polygon poly = ref2->obj ().transformed (tn * si.trans () * db::ICplxTrans (ref2->trans ())); - (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_layout->shape_repository())); + // NOTE: we intentionally rewrite to the subject layout - this way polygon refs in the context come from the + // subject, not from the intruder. + (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_subject_layout->shape_repository())); ++si; @@ -327,7 +344,8 @@ public: } private: - db::Layout *mp_layout; + db::Layout *mp_subject_layout; + const db::Layout *mp_intruder_layout; unsigned int m_intruder_layer; db::Coord m_dist; std::map > *mp_result; @@ -399,7 +417,9 @@ struct InteractionRegistrationInst2Inst : db::box_scanner_receiver2 { public: - InteractionRegistrationInst2Inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, db::Coord dist, std::map, std::set > > *result) + typedef std::pair, std::set > interaction_value_type; + + InteractionRegistrationInst2Inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, db::Coord dist, std::map *result) : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result) { // nothing yet .. @@ -410,7 +430,7 @@ public: // @@@ TODO: always insert, if both instances come from different layouts // NOTE: self-interactions are possible for arrays: different elements of the // array may interact which is a cell-external interaction. - if ((*inst1 != *inst2 || inst1->size () > 1) + if ((mp_subject_layout != mp_intruder_layout || *inst1 != *inst2 || inst1->size () > 1) && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer, m_dist)) { (*mp_result) [inst1].first.insert (inst2); } @@ -482,7 +502,13 @@ private: // LocalProcessor implementation LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top) - : mp_layout (layout), mp_top (top) + : mp_subject_layout (layout), mp_intruder_layout (layout), mp_subject_top (top), mp_intruder_top (top) +{ + // .. nothing yet .. +} + +LocalProcessor::LocalProcessor (db::Layout *subject_layout, db::Cell *subject_top, const db::Layout *intruder_layout, const db::Cell *intruder_top) + : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), mp_subject_top (subject_top), mp_intruder_top (intruder_top) { // .. nothing yet .. } @@ -511,57 +537,79 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const L contexts.set_description (op->description ()); std::pair, std::set > intruders; - compute_contexts (contexts, 0, 0, mp_top, db::ICplxTrans (), intruders, op->dist ()); + compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ()); } -void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders, db::Coord dist) +void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, + db::LocalProcessorCellContext *parent_context, + db::Cell *subject_parent, + db::Cell *subject_cell, + const db::ICplxTrans &subject_cell_inst, + const db::Cell *intruder_cell, + const std::pair, std::set > &intruders, + db::Coord dist) { if (tl::verbosity () >= 30) { - if (! parent) { - tl::log << tr ("Computing context for top cell ") << mp_layout->cell_name (cell->cell_index ()); + if (! subject_parent) { + tl::log << tr ("Computing context for top cell ") << mp_subject_layout->cell_name (subject_cell->cell_index ()); } else { - tl::log << tr ("Computing context for ") << mp_layout->cell_name (parent->cell_index ()) << " -> " << mp_layout->cell_name (cell->cell_index ()) << " @" << cell_inst.to_string (); + tl::log << tr ("Computing context for ") << mp_subject_layout->cell_name (subject_parent->cell_index ()) << " -> " << mp_subject_layout->cell_name (subject_cell->cell_index ()) << " @" << subject_cell_inst.to_string (); } } - db::LocalProcessorCellContexts &cell_contexts = contexts.contexts_per_cell (cell); + db::LocalProcessorCellContexts &cell_contexts = contexts.contexts_per_cell (subject_cell, intruder_cell); db::LocalProcessorCellContext *context = cell_contexts.find_context (intruders); if (context) { - context->add (parent_context, parent, cell_inst); + context->add (parent_context, subject_parent, subject_cell_inst); return; } context = cell_contexts.create (intruders); - context->add (parent_context, parent, cell_inst); + context->add (parent_context, subject_parent, subject_cell_inst); - const db::Shapes &shapes_intruders = cell->shapes (contexts.intruder_layer ()); + const db::Shapes *intruder_shapes = 0; + if (intruder_cell) { + intruder_shapes = &intruder_cell->shapes (contexts.intruder_layer ()); + } - db::box_convert inst_bcs (*mp_layout, contexts.subject_layer ()); - db::box_convert inst_bci (*mp_layout, contexts.intruder_layer ()); - db::box_convert inst_bcii (*mp_layout, contexts.intruder_layer ()); + db::box_convert inst_bcs (*mp_subject_layout, contexts.subject_layer ()); + db::box_convert inst_bci (*mp_intruder_layout, contexts.intruder_layer ()); + db::box_convert inst_bcii (*mp_intruder_layout, contexts.intruder_layer ()); - if (! cell->begin ().at_end ()) { + // handle top-down interactions (subject instances interacting with intruder shapes) + // and sibling interactions + + if (! subject_cell->begin ().at_end ()) { typedef std::pair, std::set > interaction_value_type; std::map interactions; // insert dummy interactions to handle at least the child cell vs. itself - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ())); + // - this is important so we will always handle the instances unless they are + // entirely empty in the subject layer + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { + if (! inst_bcs (i->cell_inst ()).empty ()) { + interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ())); + } } { db::box_scanner2 scanner; - InteractionRegistrationInst2Inst rec (mp_layout, contexts.subject_layer (), mp_layout, contexts.intruder_layer (), dist, &interactions); + InteractionRegistrationInst2Inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.intruder_layer (), dist, &interactions); - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { if (! inst_bcs (i->cell_inst ()).empty ()) { scanner.insert1 (&i->cell_inst (), 0); } - if (! inst_bci (i->cell_inst ()).empty ()) { - scanner.insert2 (&i->cell_inst (), 0); + } + + if (intruder_cell) { + for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { + if (! inst_bci (i->cell_inst ()).empty ()) { + scanner.insert2 (&i->cell_inst (), 0); + } } } @@ -576,9 +624,9 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Loc { db::box_scanner2 scanner; - InteractionRegistrationInst2Shape rec (mp_layout, contexts.subject_layer (), dist, &interactions); + InteractionRegistrationInst2Shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { if (! inst_bcs (i->cell_inst ()).empty ()) { scanner.insert1 (&i->cell_inst (), 0); } @@ -587,22 +635,25 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Loc for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert2 (i.operator-> (), 0); } - for (db::Shapes::shape_iterator i = shapes_intruders.begin (polygon_ref_flags ()); !i.at_end (); ++i) { - scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + + if (intruder_shapes) { + for (db::Shapes::shape_iterator i = intruder_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } } scanner.process (rec, dist, inst_bcs, db::box_convert ()); } - for (std::map, std::set > >::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (std::map::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { - db::Cell &child_cell = mp_layout->cell (i->first->object ().cell_index ()); + db::Cell &subject_child_cell = mp_subject_layout->cell (i->first->object ().cell_index ()); for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) { db::ICplxTrans tn = i->first->complex_trans (*n); db::ICplxTrans tni = tn.inverted (); - db::Box nbox = tn * child_cell.bbox (contexts.subject_layer ()).enlarged (db::Vector (dist, dist)); + db::Box nbox = tn * subject_child_cell.bbox (contexts.subject_layer ()).enlarged (db::Vector (dist, dist)); if (! nbox.empty ()) { @@ -612,7 +663,9 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Loc for (std::set::const_iterator p = i->second.second.begin (); p != i->second.second.end (); ++p) { if (nbox.overlaps (p->box ())) { db::Polygon poly = p->obj ().transformed (tni * db::ICplxTrans (p->trans ())); - intruders_below.second.insert (db::PolygonRef (poly, mp_layout->shape_repository ())); + // NOTE: we intentionally transform into the *subject* layout so the intruders are local to + // the subject layout. + intruders_below.second.insert (db::PolygonRef (poly, mp_subject_layout->shape_repository ())); } } @@ -624,7 +677,8 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Loc } } - compute_contexts (contexts, context, cell, &child_cell, tn, intruders_below, dist); + db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? &subject_child_cell : 0); + compute_contexts (contexts, context, subject_cell, &subject_child_cell, tn, intruder_child_cell, intruders_below, dist); } @@ -641,12 +695,12 @@ LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOp tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing results for ")) + description ()); // avoids updates while we work on the layout - mp_layout->update (); - db::LayoutLocker locker (mp_layout); + mp_subject_layout->update (); + db::LayoutLocker locker (mp_subject_layout); - for (db::Layout::bottom_up_const_iterator bu = mp_layout->begin_bottom_up (); bu != mp_layout->end_bottom_up (); ++bu) { + for (db::Layout::bottom_up_const_iterator bu = mp_subject_layout->begin_bottom_up (); bu != mp_subject_layout->end_bottom_up (); ++bu) { - LocalProcessorContexts::iterator cpc = contexts.context_map ().find (&mp_layout->cell (*bu)); + LocalProcessorContexts::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu)); if (cpc != contexts.context_map ().end ()) { cpc->second.compute_results (contexts, cpc->first, op, output_layer, this); contexts.context_map ().erase (cpc); @@ -656,57 +710,70 @@ LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOp } void -LocalProcessor::compute_local_cell (LocalProcessorContexts &contexts, db::Cell *cell, const db::LocalOperation *op, const std::pair, std::set > &intruders, std::set &result) +LocalProcessor::compute_local_cell (LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const db::LocalOperation *op, const std::pair, std::set > &intruders, std::set &result) { - const db::Shapes &shapes_subject = cell->shapes (contexts.subject_layer ()); - const db::Shapes &shapes_intruders = cell->shapes (contexts.intruder_layer ()); + const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ()); + + const db::Shapes *intruder_shapes = 0; + if (intruder_cell) { + intruder_shapes = &intruder_cell->shapes (contexts.intruder_layer ()); + if (intruder_shapes->empty ()) { + intruder_shapes = 0; + } + } // local shapes vs. child cell std::map > interactions; - db::box_convert inst_bci (*mp_layout, contexts.intruder_layer ()); + db::box_convert inst_bci (*mp_intruder_layout, contexts.intruder_layer ()); if (op->on_empty_intruder_hint () != LocalOperation::Drop) { // insert dummy interactions to accommodate subject vs. nothing - for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + for (db::Shapes::shape_iterator i = subject_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); } } - if (! shapes_subject.empty () && ! (shapes_intruders.empty () && intruders.second.empty ())) { + if (! subject_shapes->empty () && (intruder_shapes || ! intruders.second.empty ())) { db::box_scanner2 scanner; - InteractionRegistrationShape2Shape rec (&interactions); + InteractionRegistrationShape2Shape rec (mp_subject_layout == mp_intruder_layout ? 0 : mp_subject_layout, &interactions); - for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + for (db::Shapes::shape_iterator i = subject_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); } for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert2 (i.operator-> (), 0); } - for (db::Shapes::shape_iterator i = shapes_intruders.begin (polygon_ref_flags ()); !i.at_end (); ++i) { - scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + + if (intruder_shapes) { + for (db::Shapes::shape_iterator i = intruder_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } } scanner.process (rec, op->dist (), db::box_convert (), db::box_convert ()); } - if (! shapes_subject.empty () && ! (cell->begin ().at_end () && intruders.first.empty ())) { + if (! subject_shapes->empty () && ! ((! intruder_cell || intruder_cell->begin ().at_end ()) && intruders.first.empty ())) { db::box_scanner2 scanner; - InteractionRegistrationShape2Inst rec (mp_layout, contexts.intruder_layer (), op->dist (), &interactions); + InteractionRegistrationShape2Inst rec (mp_subject_layout, mp_intruder_layout, contexts.intruder_layer (), op->dist (), &interactions); - for (db::Shapes::shape_iterator i = shapes_subject.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + for (db::Shapes::shape_iterator i = subject_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); } - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - if (! inst_bci (i->cell_inst ()).empty ()) { - scanner.insert2 (&i->cell_inst (), 0); + if (intruder_cell) { + for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { + if (! inst_bci (i->cell_inst ()).empty ()) { + scanner.insert2 (&i->cell_inst (), 0); + } } } + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { if (! inst_bci (*i).empty ()) { scanner.insert2 (i.operator-> (), 0); @@ -717,7 +784,7 @@ LocalProcessor::compute_local_cell (LocalProcessorContexts &contexts, db::Cell * } - op->compute_local (mp_layout, interactions, result); + op->compute_local (mp_subject_layout, interactions, result); } } diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index 56c7a0b20..07418d6af 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -117,6 +117,7 @@ public: typedef map_type::const_iterator iterator; LocalProcessorCellContexts (); + LocalProcessorCellContexts (const db::Cell *intruder_cell); db::LocalProcessorCellContext *find_context (const key_type &intruders); db::LocalProcessorCellContext *create (const key_type &intruders); @@ -133,6 +134,7 @@ public: } private: + const db::Cell *mp_intruder_cell; std::map m_contexts; }; @@ -153,9 +155,13 @@ public: m_contexts_per_cell.clear (); } - LocalProcessorCellContexts &contexts_per_cell (db::Cell *cell) + LocalProcessorCellContexts &contexts_per_cell (db::Cell *subject_cell, const db::Cell *intruder_cell) { - return m_contexts_per_cell [cell]; + contexts_per_cell_type::iterator ctx = m_contexts_per_cell.find (subject_cell); + if (ctx == m_contexts_per_cell.end ()) { + ctx = m_contexts_per_cell.insert (std::make_pair (subject_cell, LocalProcessorCellContexts (intruder_cell))).first; + } + return ctx->second; } contexts_per_cell_type &context_map () @@ -213,6 +219,7 @@ class DB_PLUGIN_PUBLIC LocalProcessor { public: LocalProcessor (db::Layout *layout, db::Cell *top); + LocalProcessor (db::Layout *subject_layout, db::Cell *subject_top, const db::Layout *intruder_layout, const db::Cell *intruder_cell); void run (LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer); void compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer); void compute_results (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int output_layer); @@ -230,13 +237,15 @@ public: private: friend class LocalProcessorCellContexts; - db::Layout *mp_layout; - db::Cell *mp_top; + db::Layout *mp_subject_layout; + const db::Layout *mp_intruder_layout; + db::Cell *mp_subject_top; + const db::Cell *mp_intruder_top; std::string m_description; - void compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders, db::Coord dist); + void compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::set > &intruders, db::Coord dist); void push_results (db::Cell *cell, unsigned int output_layer, const std::set &result) const; - void compute_local_cell (LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, const std::pair, std::set > &intruders, std::set &result); + void compute_local_cell (LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const std::pair, std::set > &intruders, std::set &result); }; } diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index 7034f412f..1e799eea9 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -110,7 +110,7 @@ std::string contexts_to_s (db::Layout *layout, db::LocalProcessorContexts &conte return res; } -void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0) +void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc, bool single) { db::Layout layout_org; @@ -149,21 +149,51 @@ void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int ou normalize_layer (layout_org, l2); db::BoolAndOrNotLocalOperation op (mode == TMAnd); - db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); - if (! context_doc) { - proc.run (&op, l1, l2, lout); + if (single) { + + db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); + + if (! context_doc) { + proc.run (&op, l1, l2, lout); + } else { + db::LocalProcessorContexts contexts; + proc.compute_contexts (contexts, &op, l1, l2); + *context_doc = contexts_to_s (&layout_org, contexts); + proc.compute_results (contexts, &op, lout); + } + } else { - db::LocalProcessorContexts contexts; - proc.compute_contexts (contexts, &op, l1, l2); - *context_doc = contexts_to_s (&layout_org, contexts); - proc.compute_results (contexts, &op, lout); + + db::Layout layout_org2 = layout_org; + + db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &layout_org2, &layout_org2.cell (*layout_org2.begin_top_down ())); + + if (! context_doc) { + proc.run (&op, l1, l2, lout); + } else { + db::LocalProcessorContexts contexts; + proc.compute_contexts (contexts, &op, l1, l2); + *context_doc = contexts_to_s (&layout_org, contexts); + proc.compute_results (contexts, &op, lout); + } + } db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); } -void run_test_bool_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0) +void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0) +{ + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true); +} + +void run_test_bool2 (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0) +{ + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false); +} + +void run_test_bool_with_size_gen (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc, bool single) { db::Layout layout_org; @@ -202,20 +232,50 @@ void run_test_bool_with_size (tl::TestBase *_this, const char *file, TestMode mo normalize_layer (layout_org, l2); BoolAndOrNotWithSizedLocalOperation op (mode == TMAnd, dist); - db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); - if (! context_doc) { - proc.run (&op, l1, l2, lout); + if (single) { + + db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); + + if (! context_doc) { + proc.run (&op, l1, l2, lout); + } else { + db::LocalProcessorContexts contexts; + proc.compute_contexts (contexts, &op, l1, l2); + *context_doc = contexts_to_s (&layout_org, contexts); + proc.compute_results (contexts, &op, lout); + } + } else { - db::LocalProcessorContexts contexts; - proc.compute_contexts (contexts, &op, l1, l2); - *context_doc = contexts_to_s (&layout_org, contexts); - proc.compute_results (contexts, &op, lout); + + db::Layout layout_org2 = layout_org; + + db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &layout_org2, &layout_org2.cell (*layout_org2.begin_top_down ())); + + if (! context_doc) { + proc.run (&op, l1, l2, lout); + } else { + db::LocalProcessorContexts contexts; + proc.compute_contexts (contexts, &op, l1, l2); + *context_doc = contexts_to_s (&layout_org, contexts); + proc.compute_results (contexts, &op, lout); + } + } db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); } +void run_test_bool_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0) +{ + run_test_bool_with_size_gen (_this, file, mode, dist, out_layer_num, context_doc, true); +} + +void run_test_bool2_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0) +{ + run_test_bool_with_size_gen (_this, file, mode, dist, out_layer_num, context_doc, false); +} + TEST(BasicAnd1) { // Simple flat AND @@ -496,3 +556,282 @@ TEST(BasicNotWithSize10) run_test_bool_with_size (_this, "hlp10.oas", TMNot, 150, 103); } +TEST(TwoInputsAnd1) +{ + // Simple flat AND + run_test_bool2 (_this, "hlp1.oas", TMAnd, 100); +} + +TEST(TwoInputsNot1) +{ + // Simple flat NOT + run_test_bool2 (_this, "hlp1.oas", TMNot, 101); +} + +TEST(TwoInputsAnd2) +{ + // Up/down and down/up interactions, AND + run_test_bool2 (_this, "hlp2.oas", TMAnd, 100); +} + +TEST(TwoInputsNot2) +{ + // Up/down and down/up interactions, NOT + run_test_bool2 (_this, "hlp2.oas", TMNot, 101); +} + +TEST(TwoInputsAnd3) +{ + // Variant building, AND + run_test_bool2 (_this, "hlp3.oas", TMAnd, 100); +} + +TEST(TwoInputsNot3) +{ + // Variant building, NOT + run_test_bool2 (_this, "hlp3.oas", TMNot, 101); +} + +TEST(TwoInputsAnd4) +{ + // Sibling interactions, variant building, AND + run_test_bool2 (_this, "hlp4.oas", TMAnd, 100); +} + +TEST(TwoInputsNot4) +{ + // Sibling interactions, variant building, NOT + run_test_bool2 (_this, "hlp4.oas", TMNot, 101); +} + +TEST(TwoInputsAnd5) +{ + // Variant building with intermediate hierarchy, AND + run_test_bool2 (_this, "hlp5.oas", TMAnd, 100); +} + +TEST(TwoInputsNot5) +{ + // Variant building with intermediate hierarchy, NOT + run_test_bool2 (_this, "hlp5.oas", TMNot, 101); +} + +TEST(TwoInputsAnd6) +{ + // Extreme variants (copy, vanishing), AND + run_test_bool2 (_this, "hlp6.oas", TMAnd, 100); +} + +TEST(TwoInputsNot6) +{ + // Extreme variants (copy, vanishing), NOT + run_test_bool2 (_this, "hlp6.oas", TMNot, 101); +} + +TEST(TwoInputsAnd7) +{ + // Context replication - direct and indirect, AND + run_test_bool2 (_this, "hlp7.oas", TMAnd, 100); +} + +TEST(TwoInputsNot7) +{ + // Context replication - direct and indirect, NOT + run_test_bool2 (_this, "hlp7.oas", TMNot, 101); +} + +TEST(TwoInputsAnd8) +{ + // Mixed sibling-parent contexts, AND + run_test_bool2 (_this, "hlp8.oas", TMAnd, 100); +} + +TEST(TwoInputsNot8) +{ + // Mixed sibling-parent contexts, NOT + run_test_bool2 (_this, "hlp8.oas", TMNot, 101); +} + +TEST(TwoInputsAnd9) +{ + // Top-level ring structure, AND + std::string doc; + run_test_bool2 (_this, "hlp9.oas", TMAnd, 100, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + ); +} + +TEST(TwoInputsNot9) +{ + // Top-level ring structure, NOT + std::string doc; + run_test_bool2 (_this, "hlp9.oas", TMNot, 101, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + ); +} + +TEST(TwoInputsAnd10) +{ + // Array instances, AND + run_test_bool2 (_this, "hlp10.oas", TMAnd, 100); +} + +TEST(TwoInputsNot10) +{ + // Array instances, NOT + run_test_bool2 (_this, "hlp10.oas", TMNot, 101); +} + +TEST(TwoInputsAndWithSize1) +{ + // Simple flat AND + run_test_bool2_with_size (_this, "hlp1.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize1) +{ + // Simple flat NOT + run_test_bool2_with_size (_this, "hlp1.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize2) +{ + // Up/down and down/up interactions, AND + run_test_bool2_with_size (_this, "hlp2.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize2) +{ + // Up/down and down/up interactions, NOT + run_test_bool2_with_size (_this, "hlp2.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize3) +{ + // Variant building, AND + run_test_bool2_with_size (_this, "hlp3.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize3) +{ + // Variant building, NOT + run_test_bool2_with_size (_this, "hlp3.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize4) +{ + // Sibling interactions, variant building, AND + run_test_bool2_with_size (_this, "hlp4.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize4) +{ + // Sibling interactions, variant building, NOT + run_test_bool2_with_size (_this, "hlp4.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize5) +{ + // Variant building with intermediate hierarchy, AND + run_test_bool2_with_size (_this, "hlp5.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize5) +{ + // Variant building with intermediate hierarchy, NOT + run_test_bool2_with_size (_this, "hlp5.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize6) +{ + // Extreme variants (copy, vanishing), AND + run_test_bool2_with_size (_this, "hlp6.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize6) +{ + // Extreme variants (copy, vanishing), NOT + run_test_bool2_with_size (_this, "hlp6.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize7) +{ + // Context replication - direct and indirect, AND + run_test_bool2_with_size (_this, "hlp7.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize7) +{ + // Context replication - direct and indirect, NOT + run_test_bool2_with_size (_this, "hlp7.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize8) +{ + // Mixed sibling-parent contexts, AND + run_test_bool2_with_size (_this, "hlp8.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize8) +{ + // Mixed sibling-parent contexts, NOT + run_test_bool2_with_size (_this, "hlp8.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize9) +{ + // Top-level ring structure, AND + std::string doc; + run_test_bool2_with_size (_this, "hlp9.oas", TMAnd, 1500, 102, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + ); +} + +TEST(TwoInputsNotWithSize9) +{ + // Top-level ring structure, NOT + std::string doc; + run_test_bool2_with_size (_this, "hlp9.oas", TMNot, 1500, 103, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + ); +} + +TEST(TwoInputsAndWithSize10) +{ + // Array instances, AND + run_test_bool2_with_size (_this, "hlp10.oas", TMAnd, 150, 102); +} + +TEST(TwoInputsNotWithSize10) +{ + // Array instances, NOT + run_test_bool2_with_size (_this, "hlp10.oas", TMNot, 150, 103); +} From 2e619983729714589e1bdb1d8e37aa6c4c668cf4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Oct 2018 01:40:01 +0200 Subject: [PATCH 026/335] WIP: interactions with same layer (needs finishing) --- .../tools/netx/db_plugin/dbHierProcessor.cc | 374 ++++++++++++++---- .../tools/netx/db_plugin/dbHierProcessor.h | 56 ++- src/plugins/tools/netx/testdata/hlp1.oas | Bin 691 -> 740 bytes src/plugins/tools/netx/testdata/hlp2.oas | Bin 1039 -> 1134 bytes src/plugins/tools/netx/testdata/hlp3.oas | Bin 755 -> 817 bytes src/plugins/tools/netx/testdata/hlp4.oas | Bin 768 -> 830 bytes src/plugins/tools/netx/testdata/hlp5.oas | Bin 868 -> 936 bytes src/plugins/tools/netx/testdata/hlp6.oas | Bin 1298 -> 1468 bytes src/plugins/tools/netx/testdata/hlp7.oas | Bin 795 -> 839 bytes src/plugins/tools/netx/testdata/hlp8.oas | Bin 955 -> 1026 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 193 ++++++++- 11 files changed, 533 insertions(+), 90 deletions(-) diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 94fb1435d..39405385f 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -72,6 +72,8 @@ BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and) // .. nothing yet .. } +// --------------------------------------------------------------------------------------------- + LocalOperation::on_empty_intruder_mode BoolAndOrNotLocalOperation::on_empty_intruder_hint () const { @@ -85,36 +87,36 @@ BoolAndOrNotLocalOperation::description () const } void -BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const +BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const { db::EdgeProcessor ep; size_t p1 = 0, p2 = 1; std::set others; - for (std::map >::const_iterator r = interactions.begin (); r != interactions.end (); ++r) { - - // TODO: vector could be set - bool found = false; - for (std::vector::const_iterator i = r->second.begin (); i != r->second.end () && ! found; ++i) { - found = (*i == r->first); + for (ShapeInteractions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (ShapeInteractions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.shape (*j)); } - if (found) { - // shortcut (and: keep, not: drop) + } + + for (ShapeInteractions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::PolygonRef &subject = interactions.shape (i->first); + if (others.find (subject) != others.end ()) { if (m_is_and) { - result.insert (r->first); + result.insert (subject); } - } else if (r->second.empty ()) { + } else if (i->second.empty ()) { // shortcut (not: keep, and: drop) if (! m_is_and) { - result.insert (r->first); + result.insert (subject); } } else { - for (db::PolygonRef::polygon_edge_iterator e = r->first.begin_edge (); ! e.at_end(); ++e) { + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { ep.insert (*e, p1); } p1 += 2; - others.insert (r->second.begin (), r->second.end ()); } } @@ -136,6 +138,64 @@ BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const std::map &result) const +{ + if (m_wrap_count == 0) { + return; + } + + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + std::set seen2; + + for (ShapeInteractions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::PolygonRef &subject = interactions.shape (i->first); + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + + for (db::ShapeInteractions::iterator2 o = i->second.begin (); o != i->second.end (); ++o) { + // don't take the same (really the same, not an identical one) shape twice - the interaction + // set does not take care to list just one copy of the same item on the intruder side. + if (seen2.find (*o) == seen2.end ()) { + seen2.insert (*o); + const db::PolygonRef &intruder = interactions.shape (*o); + for (db::PolygonRef::polygon_edge_iterator e = intruder.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + } + + } + + db::MergeOp op (m_wrap_count - 1); + db::PolygonRefGenerator pr (layout, result); + db::PolygonGenerator pg (pr, true, true); + ep.process (pg, op); +} + +SelfOverlapMergeLocalOperation::on_empty_intruder_mode SelfOverlapMergeLocalOperation::on_empty_intruder_hint () const +{ + return m_wrap_count > 1 ? LocalOperation::Drop : LocalOperation::Copy; +} + +std::string SelfOverlapMergeLocalOperation::description () const +{ + return tl::sprintf (tl::to_string (tr ("Self-overlap (wrap count %d)")), int (m_wrap_count)); +} + // --------------------------------------------------------------------------------------------- // LocalProcessorCellContext implementation @@ -262,6 +322,63 @@ LocalProcessorCellContexts::compute_results (LocalProcessorContexts &contexts, d proc->push_results (cell, output_layer, common); } +// --------------------------------------------------------------------------------------------- + +ShapeInteractions::ShapeInteractions () + : m_id (0) +{ + // .. nothing yet .. +} + +bool +ShapeInteractions::has_shape_id (unsigned int id) const +{ + return m_shapes.find (id) != m_shapes.end (); +} + +void +ShapeInteractions::add_shape (unsigned int id, const db::PolygonRef &shape) +{ + m_shapes [id] = shape; +} + +void +ShapeInteractions::add_subject (unsigned int id, const db::PolygonRef &shape) +{ + add_shape (id, shape); + m_interactions.insert (std::make_pair (id, container::value_type::second_type ())); +} + +void +ShapeInteractions::add_interaction (unsigned int subject_id, unsigned int intruder_id) +{ + m_interactions [subject_id].push_back (intruder_id); +} + +const std::vector & +ShapeInteractions::intruders_for (unsigned int subject_id) const +{ + iterator i = m_interactions.find (subject_id); + if (i == m_interactions.end ()) { + static std::vector empty; + return empty; + } else { + return i->second; + } +} + +const db::PolygonRef & +ShapeInteractions::shape (unsigned int id) const +{ + std::map::const_iterator i = m_shapes.find (id); + if (i == m_shapes.end ()) { + static db::PolygonRef s; + return s; + } else { + return i->second; + } +} + // --------------------------------------------------------------------------------------------- // Helper classes for the LocalProcessor @@ -274,46 +391,73 @@ inline unsigned int polygon_ref_flags () } struct InteractionRegistrationShape2Shape - : db::box_scanner_receiver2 + : db::box_scanner_receiver2 { public: - InteractionRegistrationShape2Shape (db::Layout *layout, std::map > *result) + InteractionRegistrationShape2Shape (db::Layout *layout, ShapeInteractions *result) : mp_result (result), mp_layout (layout) { // nothing yet .. } - void add (const db::PolygonRef *ref1, int, const db::PolygonRef *ref2, int) + void add (const db::PolygonRef *ref1, unsigned int id1, const db::PolygonRef *ref2, unsigned int id2) { + mp_result->add_shape (id1, *ref1); + if (mp_layout) { // In order to guarantee the refs come from the subject layout, we'd need to // rewrite them to the subject layout if required. - db::Polygon poly = ref2->obj ().transformed (ref2->trans ()); - (*mp_result) [*ref1].push_back (db::PolygonRef (poly, mp_layout->shape_repository ())); + if (!mp_result->has_shape_id (id2)) { + db::Polygon poly = ref2->obj ().transformed (ref2->trans ()); + mp_result->add_shape (id2, db::PolygonRef (poly, mp_layout->shape_repository ())); + } } else { - (*mp_result) [*ref1].push_back (*ref2); + mp_result->add_shape (id2, *ref2); } + mp_result->add_interaction (id1, id2); } private: - std::map > *mp_result; + ShapeInteractions *mp_result; db::Layout *mp_layout; }; -struct InteractionRegistrationShape2Inst - : db::box_scanner_receiver2 +struct InteractionRegistrationShape1 + : db::box_scanner_receiver { public: - InteractionRegistrationShape2Inst (db::Layout *subject_layout, const db::Layout *intruder_layout, unsigned int intruder_layer, db::Coord dist, std::map > *result) + InteractionRegistrationShape1 (ShapeInteractions *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const db::PolygonRef *ref1, unsigned int id1, const db::PolygonRef *ref2, unsigned int id2) + { + mp_result->add_shape (id1, *ref1); + mp_result->add_shape (id2, *ref2); + mp_result->add_interaction (id1, id2); + } + +private: + ShapeInteractions *mp_result; +}; + +struct InteractionRegistrationShape2Inst + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationShape2Inst (db::Layout *subject_layout, const db::Layout *intruder_layout, unsigned int intruder_layer, db::Coord dist, ShapeInteractions *result) : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result) { // nothing yet .. } - void add (const db::PolygonRef *ref, int, const db::CellInstArray *inst, int) + void add (const db::PolygonRef *ref, unsigned int id1, const db::CellInstArray *inst, unsigned int inst_id) { const db::Cell &intruder_cell = mp_intruder_layout->cell (inst->object ().cell_index ()); db::box_convert inst_bc (*mp_intruder_layout, m_intruder_layer); + mp_result->add_shape (id1, *ref); for (db::CellInstArray::iterator n = inst->begin_touching (ref->box ().enlarged (db::Vector (m_dist - 1, m_dist - 1)), inst_bc); !n.at_end (); ++n) { @@ -327,12 +471,23 @@ public: si.shape_flags (polygon_ref_flags ()); while (! si.at_end ()) { - // @@@ it should be easier to transform references const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); - db::Polygon poly = ref2->obj ().transformed (tn * si.trans () * db::ICplxTrans (ref2->trans ())); - // NOTE: we intentionally rewrite to the subject layout - this way polygon refs in the context come from the - // subject, not from the intruder. - (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_subject_layout->shape_repository())); + + // reuse the same id for shapes from the same instance -> this avoid duplicates with different IDs on + // the intruder side. + std::map, unsigned int>::const_iterator k = m_inst_shape_ids.find (std::make_pair (inst_id, ref2)); + if (k == m_inst_shape_ids.end ()) { + + k = m_inst_shape_ids.insert (std::make_pair (std::make_pair (inst_id, ref2), mp_result->next_id ())).first; + + db::Polygon poly = ref2->obj ().transformed (tn * si.trans () * db::ICplxTrans (ref2->trans ())); + // NOTE: we intentionally rewrite to the subject layout - this way polygon refs in the context come from the + // subject, not from the intruder. + mp_result->add_shape (k->second, db::PolygonRef (poly, mp_subject_layout->shape_repository())); + + } + + mp_result->add_interaction (id1, k->second); ++si; @@ -348,7 +503,8 @@ private: const db::Layout *mp_intruder_layout; unsigned int m_intruder_layer; db::Coord m_dist; - std::map > *mp_result; + ShapeInteractions *mp_result; + std::map, unsigned int> m_inst_shape_ids; }; static bool @@ -414,7 +570,7 @@ instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, u } struct InteractionRegistrationInst2Inst - : db::box_scanner_receiver2 + : db::box_scanner_receiver2 { public: typedef std::pair, std::set > interaction_value_type; @@ -425,14 +581,26 @@ public: // nothing yet .. } - void add (const db::CellInstArray *inst1, int, const db::CellInstArray *inst2, int) + void add (const db::CellInstArray *inst1, unsigned int id1, const db::CellInstArray *inst2, unsigned int id2) { - // @@@ TODO: always insert, if both instances come from different layouts // NOTE: self-interactions are possible for arrays: different elements of the // array may interact which is a cell-external interaction. - if ((mp_subject_layout != mp_intruder_layout || *inst1 != *inst2 || inst1->size () > 1) - && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer, m_dist)) { - (*mp_result) [inst1].first.insert (inst2); + if (mp_subject_layout != mp_intruder_layout || id1 != id2 || inst1->size () > 1) { + + bool ignore = false; + if (mp_subject_layout == mp_intruder_layout && m_subject_layer == m_intruder_layer) { + if (m_interactions.find (std::make_pair (id2, id1)) != m_interactions.end ()) { + // for self interactions ignore the reverse interactions + ignore = true; + } else { + m_interactions.insert (std::make_pair (id1, id2)); + } + } + + if (! ignore && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer, m_dist)) { + (*mp_result) [inst1].first.insert (inst2); + } + } } @@ -441,6 +609,7 @@ private: unsigned int m_subject_layer, m_intruder_layer; db::Coord m_dist; std::map, std::set > > *mp_result; + std::set > m_interactions; }; static bool @@ -473,7 +642,7 @@ instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *ins } struct InteractionRegistrationInst2Shape - : db::box_scanner_receiver2 + : db::box_scanner_receiver2 { public: InteractionRegistrationInst2Shape (const db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, std::map, std::set > > *result) @@ -482,7 +651,7 @@ public: // nothing yet .. } - void add (const db::CellInstArray *inst, int, const db::PolygonRef *ref, int) + void add (const db::CellInstArray *inst, unsigned int, const db::PolygonRef *ref, unsigned int) { if (instance_shape_interacts (mp_subject_layout, inst, m_subject_layer, *ref, m_dist)) { (*mp_result) [inst].second.insert (*ref); @@ -599,23 +768,44 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::box_scanner2 scanner; InteractionRegistrationInst2Inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.intruder_layer (), dist, &interactions); - for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { - if (! inst_bcs (i->cell_inst ()).empty ()) { - scanner.insert1 (&i->cell_inst (), 0); - } - } + unsigned int id = 0; - if (intruder_cell) { - for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { + if (subject_cell == intruder_cell) { + + // Use the same id's for same instances - this way we can easily detect same instances + // and don't make the self-interacting + + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { + unsigned int iid = ++id; + if (! inst_bcs (i->cell_inst ()).empty ()) { + scanner.insert1 (&i->cell_inst (), iid); + } if (! inst_bci (i->cell_inst ()).empty ()) { - scanner.insert2 (&i->cell_inst (), 0); + scanner.insert2 (&i->cell_inst (), iid); } } + + } else { + + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { + if (! inst_bcs (i->cell_inst ()).empty ()) { + scanner.insert1 (&i->cell_inst (), ++id); + } + } + + if (intruder_cell) { + for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { + if (! inst_bci (i->cell_inst ()).empty ()) { + scanner.insert2 (&i->cell_inst (), ++id); + } + } + } + } for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { if (! inst_bci (*i).empty ()) { - scanner.insert2 (i.operator-> (), 0); + scanner.insert2 (i.operator-> (), ++id); } } @@ -724,36 +914,69 @@ LocalProcessor::compute_local_cell (LocalProcessorContexts &contexts, db::Cell * // local shapes vs. child cell - std::map > interactions; + ShapeInteractions interactions; db::box_convert inst_bci (*mp_intruder_layout, contexts.intruder_layer ()); - if (op->on_empty_intruder_hint () != LocalOperation::Drop) { - // insert dummy interactions to accommodate subject vs. nothing - for (db::Shapes::shape_iterator i = subject_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { - interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); + // insert dummy interactions to accommodate subject vs. nothing and assign an ID + // range for the subject shapes. + unsigned int subject_id0 = 0; + for (db::Shapes::shape_iterator i = subject_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { + + unsigned int id = interactions.next_id (); + if (subject_id0 == 0) { + subject_id0 = id; } + + if (op->on_empty_intruder_hint () != LocalOperation::Drop) { + const db::PolygonRef *ref = i->basic_ptr (db::PolygonRef::tag ()); + interactions.add_subject (id, *ref); + } + } if (! subject_shapes->empty () && (intruder_shapes || ! intruders.second.empty ())) { - db::box_scanner2 scanner; - InteractionRegistrationShape2Shape rec (mp_subject_layout == mp_intruder_layout ? 0 : mp_subject_layout, &interactions); + if (subject_cell == intruder_cell && contexts.subject_layer () == contexts.intruder_layer ()) { - for (db::Shapes::shape_iterator i = subject_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { - scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); - } + db::box_scanner scanner; + InteractionRegistrationShape1 rec (&interactions); - for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); - } - - if (intruder_shapes) { - for (db::Shapes::shape_iterator i = intruder_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { - scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + unsigned int id = subject_id0; + for (db::Shapes::shape_iterator i = subject_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { + const db::PolygonRef *ref = i->basic_ptr (db::PolygonRef::tag ()); + scanner.insert (ref, id++); } - } - scanner.process (rec, op->dist (), db::box_convert (), db::box_convert ()); + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + scanner.insert (i.operator-> (), interactions.next_id ()); + } + + scanner.process (rec, op->dist (), db::box_convert ()); + + } else { + + db::box_scanner2 scanner; + InteractionRegistrationShape2Shape rec (mp_subject_layout == mp_intruder_layout ? 0 : mp_subject_layout, &interactions); + + unsigned int id = subject_id0; + for (db::Shapes::shape_iterator i = subject_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { + const db::PolygonRef *ref = i->basic_ptr (db::PolygonRef::tag ()); + scanner.insert1 (ref, id++); + } + + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + scanner.insert2 (i.operator-> (), interactions.next_id ()); + } + + if (intruder_shapes) { + for (db::Shapes::shape_iterator i = intruder_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), interactions.next_id ()); + } + } + + scanner.process (rec, op->dist (), db::box_convert (), db::box_convert ()); + + } } @@ -762,21 +985,30 @@ LocalProcessor::compute_local_cell (LocalProcessorContexts &contexts, db::Cell * db::box_scanner2 scanner; InteractionRegistrationShape2Inst rec (mp_subject_layout, mp_intruder_layout, contexts.intruder_layer (), op->dist (), &interactions); + unsigned int id = subject_id0; for (db::Shapes::shape_iterator i = subject_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { - scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); + scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), id++); } - if (intruder_cell) { + unsigned int inst_id = 0; + + if (subject_cell == intruder_cell && contexts.subject_layer () == contexts.intruder_layer ()) { + + // Same cell, same layer -> no shape to child instance interactions because this will be taken care of + // by the instances themselves (and their intruders). This also means, we prefer to deal with + // interactions low in the hierarchy. + + } else if (intruder_cell) { for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { if (! inst_bci (i->cell_inst ()).empty ()) { - scanner.insert2 (&i->cell_inst (), 0); + scanner.insert2 (&i->cell_inst (), ++inst_id); } } } for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { if (! inst_bci (*i).empty ()) { - scanner.insert2 (i.operator-> (), 0); + scanner.insert2 (i.operator-> (), ++inst_id); } } diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index 07418d6af..4cceeac97 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -39,6 +39,44 @@ class LocalProcessor; class LocalProcessorCellContext; class LocalProcessorContexts; +// @@@ TODO: move this somewhere else? +class DB_PLUGIN_PUBLIC ShapeInteractions +{ +public: + typedef std::map > container; + typedef container::const_iterator iterator; + typedef container::value_type::second_type::const_iterator iterator2; + + ShapeInteractions (); + + iterator begin () const + { + return m_interactions.begin (); + } + + iterator end () const + { + return m_interactions.end (); + } + + bool has_shape_id (unsigned int id) const; + void add_shape (unsigned int id, const db::PolygonRef &shape); + void add_subject (unsigned int id, const db::PolygonRef &shape); + void add_interaction (unsigned int subject_id, unsigned int intruder_id); + const std::vector &intruders_for (unsigned int subject_id) const; + const db::PolygonRef &shape (unsigned int id) const; + + unsigned int next_id () + { + return ++m_id; + } + +private: + std::map > m_interactions; + std::map m_shapes; + unsigned int m_id; +}; + class DB_PLUGIN_PUBLIC LocalOperation { public: @@ -49,7 +87,7 @@ public: LocalOperation () { } virtual ~LocalOperation () { } - virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const = 0; + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const = 0; virtual on_empty_intruder_mode on_empty_intruder_hint () const = 0; virtual std::string description () const = 0; virtual db::Coord dist () const { return 0; } @@ -61,7 +99,7 @@ class DB_PLUGIN_PUBLIC BoolAndOrNotLocalOperation public: BoolAndOrNotLocalOperation (bool is_and); - virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const; + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const; virtual on_empty_intruder_mode on_empty_intruder_hint () const; virtual std::string description () const; @@ -69,6 +107,20 @@ private: bool m_is_and; }; +class DB_PLUGIN_PUBLIC SelfOverlapMergeLocalOperation + : public LocalOperation +{ +public: + SelfOverlapMergeLocalOperation (unsigned int wrap_count); + + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const; + virtual on_empty_intruder_mode on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + unsigned int m_wrap_count; +}; + // @@@ TODO: should be hidden (private data?) struct DB_PLUGIN_PUBLIC LocalProcessorCellDrop { diff --git a/src/plugins/tools/netx/testdata/hlp1.oas b/src/plugins/tools/netx/testdata/hlp1.oas index 7dee89a076c77d0e72e4a3c29d851723d02402dc..39fe2a5dd1427b939ce55a6a6aa8e56e23c28b17 100644 GIT binary patch delta 27 jcmdnY`h<1EW=3XF%e;vX^%$8ZA7uP6`2r&^69WSPjtmJP delta 19 bcmaFDx|wyuW=2M)$sZX%PGaI=VqgFON>>IU diff --git a/src/plugins/tools/netx/testdata/hlp2.oas b/src/plugins/tools/netx/testdata/hlp2.oas index b344983c451e2db53a2bbf9fbdde0fd8d0e89a8a..bbdb6edb80fb796c03b24262a0f42fd2a3091fca 100644 GIT binary patch delta 146 zcmeC@c*ilJQoxXz&Dq1#$Hj=5ImADJmvM4Cqos+cWga8b1&tfBy`n$4L@gMZ9%z5i z`?H*JrXYxOfaing-Cc}883T~213Yu5GV(G^?EJ$A*LPtl;{vvczZJ!JnFK#DOpw_z pg;DSW7N4FVDH0PNNQ&?ph` m0gxQHlmQfb0szz&lR5)50~C1yqc;Kp0Roe81N#JkfB*o-$QL&N diff --git a/src/plugins/tools/netx/testdata/hlp3.oas b/src/plugins/tools/netx/testdata/hlp3.oas index 10ecef81b160e9b52e01652290c367dbf9e923f3..feb47dd7bc753373cf83401efc8ed84b1d4df203 100644 GIT binary patch delta 87 zcmey&x{+-|8l#%1Wga8b1&tfB7nU+EU=y`qWO|_eLGQ*RCI%4ug2n-!1F8$yCZ{p> oN`jRh;Q7HdLFR_6s0Jg`0iF+{H)b(1O=e{JFj;_!mx+M^0QNH*7XSbN delta 15 WcmdnU_L+4<8sp?1#@@w-OpE|AK?O?y diff --git a/src/plugins/tools/netx/testdata/hlp4.oas b/src/plugins/tools/netx/testdata/hlp4.oas index 9da959541d776048b6bf490829585188091ca774..508689d78384331dd520173a06f620bce6f41563 100644 GIT binary patch delta 101 zcmV-r0Gj`R2EGQ6c@QxJ217_mOhhsR15{5?4FQvI0VDwck%{F46)|p+lziZUF+o4EPF=9M~PR H^#Kb3jW`{5 delta 83 zcmV-Z0IdJM27m^Tc@Q!K217_mOhhpQ15{5?4FQp^9@ciKFP`@E7ssUnu5WO*raq@4*OioeB Xya_S~<})!(E@b*Rxq*p?iGcwC8bTH- delta 28 kcmZ3%{)BBqKI7!cj6IXhnKBufCU0c=I{5$-2NMGW0GHYcKL7v# diff --git a/src/plugins/tools/netx/testdata/hlp6.oas b/src/plugins/tools/netx/testdata/hlp6.oas index 29ec3733c6c391a929dd5a735f746569790a9a25..56db21f0ac53065c3451ab3f5d4bb594487ff39e 100644 GIT binary patch delta 51 zcmbQlwTF9y7!$9kWga8b0iGXR9qKn^C#y4>Y!+ks!^jL2o;;Z;jge`xBFp>91}uC` G3=9B;2o3K5 delta 25 hcmdnPJ&9|B7}Mr(raz2~Op_0?{G5D&g@uWM0RVCj2lfB} diff --git a/src/plugins/tools/netx/testdata/hlp7.oas b/src/plugins/tools/netx/testdata/hlp7.oas index a8c2a5649f187110296a98af298e5be80c4db14b..36664d1a9e823bfd6ba6799c4c70f86b0a4b9704 100644 GIT binary patch delta 105 zcmV-v0G9uo2FC`Fa}@?dNJ&gYF#`odPgX@z0|Qh~Pz?c-h5Oh_yO>u1ON*Fz&;WbcLD&^7SJfL Lg8~4v>j5wUQkox& delta 89 zcmX@kHk)liu_&vvf0%0!Gn=!Ar;m#vGjoW4059Xj1Amx!876j^>oEzwVEC|zg`44k rosi%U#tAYz#JQPXuy0`LXB61LB2v!8a7FBb#)f7lu!_y%Oa_bqfNU9i diff --git a/src/plugins/tools/netx/testdata/hlp8.oas b/src/plugins/tools/netx/testdata/hlp8.oas index 670bec10b96c52c1408917ee6d6a2eba04ee59a3..9b32237c9ff7f20a379f22f478157cdec583377b 100644 GIT binary patch delta 168 zcmdnZ-o!DXSdPuv!_&vbkeSulKg>0VnLRkv$=TJ%hnYFVKY*8Uass0{Bg4drXPHF} z^CrGg6y{|T{J}6mW`{U8!vQ-X!5@qa43n2LCa8+)<}orI;Q1hWLzaP;8AZ(prVoo) z7(}g8875~i1j>i5!Ha|n8hek%XCHTg2sksCWguCOm>V+lMgcen0$eWm5G4? E0Q1i-uK)l5 delta 116 zcmZqT*v&qnSdP`%Kg>0Vna$b5)5pb-nLRkv$=TJ%hnYFVKY*8U;>oj2ybKd3nhP)q zo?zf+IAA9v_=9nR%ntF%XBiU|c$uL*h|CA34~tkBM9P^Mu83XG*wD;0`3RF8v#4e2 M > &interactions, std::set &result) const + virtual void compute_local (db::Layout *layout, const db::ShapeInteractions &interactions, std::set &result) const { - std::map > sized_interactions = interactions; - for (std::map >::iterator i = sized_interactions.begin (); i != sized_interactions.end (); ++i) { - for (std::vector::iterator j = i->second.begin (); j != i->second.end (); ++j) { - db::Polygon poly = j->obj ().transformed (j->trans ()); + db::ShapeInteractions sized_interactions = interactions; + for (db::ShapeInteractions::iterator i = sized_interactions.begin (); i != sized_interactions.end (); ++i) { + for (db::ShapeInteractions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + const db::PolygonRef &ref = interactions.shape (*j); + db::Polygon poly = ref.obj ().transformed (ref.trans ()); poly.size (m_dist, m_dist); - *j = db::PolygonRef (poly, layout->shape_repository ()); + sized_interactions.add_shape (*j, db::PolygonRef (poly, layout->shape_repository ())); } } BoolAndOrNotLocalOperation::compute_local (layout, sized_interactions, result); @@ -130,8 +132,12 @@ void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, in p.layer = 2; p.datatype = 0; - lmap.map (db::LDPair (2, 0), l2 = layout_org.insert_layer ()); - layout_org.set_properties (l2, p); + if (mode == TMSelfOverlap) { + lmap.map (db::LDPair (2, 0), l2 = l1); + } else { + lmap.map (db::LDPair (2, 0), l2 = layout_org.insert_layer ()); + layout_org.set_properties (l2, p); + } p.layer = out_layer_num; p.datatype = 0; @@ -146,21 +152,30 @@ void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, in layout_org.clear_layer (lout); normalize_layer (layout_org, l1); - normalize_layer (layout_org, l2); + if (l1 != l2) { + normalize_layer (layout_org, l2); + } - db::BoolAndOrNotLocalOperation op (mode == TMAnd); + db::LocalOperation *lop = 0; + db::BoolAndOrNotLocalOperation bool_op (mode == TMAnd); + db::SelfOverlapMergeLocalOperation self_intersect_op (2); + if (mode == TMSelfOverlap) { + lop = &self_intersect_op; + } else { + lop = &bool_op; + } if (single) { db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); if (! context_doc) { - proc.run (&op, l1, l2, lout); + proc.run (lop, l1, l2, lout); } else { db::LocalProcessorContexts contexts; - proc.compute_contexts (contexts, &op, l1, l2); + proc.compute_contexts (contexts, lop, l1, l2); *context_doc = contexts_to_s (&layout_org, contexts); - proc.compute_results (contexts, &op, lout); + proc.compute_results (contexts, lop, lout); } } else { @@ -170,12 +185,12 @@ void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, in db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &layout_org2, &layout_org2.cell (*layout_org2.begin_top_down ())); if (! context_doc) { - proc.run (&op, l1, l2, lout); + proc.run (lop, l1, l2, lout); } else { db::LocalProcessorContexts contexts; - proc.compute_contexts (contexts, &op, l1, l2); + proc.compute_contexts (contexts, lop, l1, l2); *context_doc = contexts_to_s (&layout_org, contexts); - proc.compute_results (contexts, &op, lout); + proc.compute_results (contexts, lop, lout); } } @@ -835,3 +850,147 @@ TEST(TwoInputsNotWithSize10) // Array instances, NOT run_test_bool2_with_size (_this, "hlp10.oas", TMNot, 150, 103); } + +TEST(BasicSelfOverlap1) +{ + // Simple flat AND + run_test_bool (_this, "hlp1.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap2) +{ + // Up/down and down/up interactions, AND + run_test_bool (_this, "hlp2.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap3) +{ + // Variant building, AND + run_test_bool (_this, "hlp3.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap4) +{ + // Sibling interactions, variant building, AND + run_test_bool (_this, "hlp4.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap5) +{ + // Variant building with intermediate hierarchy, AND + run_test_bool (_this, "hlp5.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap6) +{ + // Extreme variants (copy, vanishing), AND + run_test_bool (_this, "hlp6.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap7) +{ + // Context replication - direct and indirect, AND + run_test_bool (_this, "hlp7.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap8) +{ + // Mixed sibling-parent contexts, AND + run_test_bool (_this, "hlp8.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap9) +{ + // Top-level ring structure, AND + std::string doc; + run_test_bool (_this, "hlp9.oas", TMSelfOverlap, 110, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + ); +} + +TEST(BasicSelfOverlap10) +{ + // Array instances, AND + run_test_bool (_this, "hlp10.oas", TMSelfOverlap, 110); +} + +#if 0 // @@@ +TEST(BasicSelfOverlapWithSize1) +{ + // Simple flat AND + run_test_bool_with_size (_this, "hlp1.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize2) +{ + // Up/down and down/up interactions, AND + run_test_bool_with_size (_this, "hlp2.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize3) +{ + // Variant building, AND + run_test_bool_with_size (_this, "hlp3.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize4) +{ + // Sibling interactions, variant building, AND + run_test_bool_with_size (_this, "hlp4.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize5) +{ + // Variant building with intermediate hierarchy, AND + run_test_bool_with_size (_this, "hlp5.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize6) +{ + // Extreme variants (copy, vanishing), AND + run_test_bool_with_size (_this, "hlp6.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize7) +{ + // Context replication - direct and indirect, AND + run_test_bool_with_size (_this, "hlp7.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize8) +{ + // Mixed sibling-parent contexts, AND + run_test_bool_with_size (_this, "hlp8.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize9) +{ + // Top-level ring structure, AND + std::string doc; + run_test_bool_with_size (_this, "hlp9.oas", TMSelfOverlap, 1500, 111, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + ); +} + +TEST(BasicSelfOverlapWithSize10) +{ + // Array instances, AND + run_test_bool_with_size (_this, "hlp10.oas", TMSelfOverlap, 150, 111); +} +#endif + + From ee55a4ca21016a75f4545bc5100f513562802fe0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Oct 2018 21:56:13 +0200 Subject: [PATCH 027/335] Fixed hier processor in case of self-interactions --- .../tools/netx/db_plugin/dbHierProcessor.cc | 24 ++++++++++++------ src/plugins/tools/netx/testdata/hlp10.oas | Bin 560 -> 584 bytes src/plugins/tools/netx/testdata/hlp9.oas | Bin 845 -> 936 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 39405385f..0828d72f0 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -155,21 +155,24 @@ void SelfOverlapMergeLocalOperation::compute_local (db::Layout *layout, const Sh db::EdgeProcessor ep; size_t p1 = 0, p2 = 1; - std::set seen2; + std::set seen; for (ShapeInteractions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const db::PolygonRef &subject = interactions.shape (i->first); - for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); + if (seen.find (i->first) == seen.end ()) { + seen.insert (i->first); + const db::PolygonRef &subject = interactions.shape (i->first); + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; } - p1 += 2; for (db::ShapeInteractions::iterator2 o = i->second.begin (); o != i->second.end (); ++o) { // don't take the same (really the same, not an identical one) shape twice - the interaction // set does not take care to list just one copy of the same item on the intruder side. - if (seen2.find (*o) == seen2.end ()) { - seen2.insert (*o); + if (seen.find (*o) == seen.end ()) { + seen.insert (*o); const db::PolygonRef &intruder = interactions.shape (*o); for (db::PolygonRef::polygon_edge_iterator e = intruder.begin_edge (); ! e.at_end(); ++e) { ep.insert (*e, p2); @@ -414,6 +417,7 @@ public: } else { mp_result->add_shape (id2, *ref2); } + mp_result->add_interaction (id1, id2); } @@ -863,7 +867,11 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, for (std::set::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) { for (db::CellInstArray::iterator k = (*j)->begin_touching (nbox.enlarged (db::Vector (-1, -1)), inst_bcii); ! k.at_end (); ++k) { - intruders_below.first.insert (db::CellInstArray (db::CellInst ((*j)->object ().cell_index ()), tni * (*j)->complex_trans (*k))); + db::ICplxTrans tk = (*j)->complex_trans (*k); + // NOTE: no self-interactions + if (i->first != *j || tn != tk) { + intruders_below.first.insert (db::CellInstArray (db::CellInst ((*j)->object ().cell_index ()), tni * tk)); + } } } diff --git a/src/plugins/tools/netx/testdata/hlp10.oas b/src/plugins/tools/netx/testdata/hlp10.oas index 9a51e38d1fcd01415bcf7b967b0002862843d52f..53aba0a970ea5a6af50942d9d27c9113478eec56 100644 GIT binary patch delta 36 pcmdnMa)M=p0i%SdVICt>hx!M$1MRG$CLrbx-i9|U42umI83Dt@3MT*n delta 20 bcmX@XvVmoT0V5;R3uULPG^T diff --git a/src/plugins/tools/netx/testdata/hlp9.oas b/src/plugins/tools/netx/testdata/hlp9.oas index c12644748b66b69ebad38ca37f68a1b1c604bb16..16d63a4f8704786db6ac1025095c4ea6a2ee8f30 100644 GIT binary patch delta 149 zcmX@hwt{`aN*z(#JVve;3Jc_40(mSi*ntd%h4L@hC$3~+U|M8t5POCoH^UqWfz7;34vYZR6fcPY delta 56 zcmZ3%ewJ;*%86HNCQoNHpB%vGHu)T*(qwri14+|VMy3X%8G;vHF>^D_kq{77OalpA LaNL~1 Date: Sat, 6 Oct 2018 22:25:24 +0200 Subject: [PATCH 028/335] Added self-overlap-with-sizing tests. --- src/plugins/tools/netx/testdata/hlp1.oas | Bin 740 -> 830 bytes src/plugins/tools/netx/testdata/hlp10.oas | Bin 584 -> 607 bytes src/plugins/tools/netx/testdata/hlp2.oas | Bin 1134 -> 1376 bytes src/plugins/tools/netx/testdata/hlp3.oas | Bin 817 -> 968 bytes src/plugins/tools/netx/testdata/hlp4.oas | Bin 830 -> 984 bytes src/plugins/tools/netx/testdata/hlp5.oas | Bin 936 -> 1141 bytes src/plugins/tools/netx/testdata/hlp6.oas | Bin 1468 -> 1819 bytes src/plugins/tools/netx/testdata/hlp7.oas | Bin 839 -> 1008 bytes src/plugins/tools/netx/testdata/hlp8.oas | Bin 1026 -> 1286 bytes src/plugins/tools/netx/testdata/hlp9.oas | Bin 936 -> 1110 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 119 +++++++++++++----- 11 files changed, 86 insertions(+), 33 deletions(-) diff --git a/src/plugins/tools/netx/testdata/hlp1.oas b/src/plugins/tools/netx/testdata/hlp1.oas index 39fe2a5dd1427b939ce55a6a6aa8e56e23c28b17..27c260c4832027592f7da843eca2ee29e52e63eb 100644 GIT binary patch delta 109 zcmaFDx{qzc6UJy!%X~(z8yORrH!9sQTOqQ-ZKm@|w;kd)&6ezBy2~wU!N}C%aDs8p zI>tYOKn}+Z*@LP-lne6Xj5-`5g^Uf7D?}J3F@A~4XXLt( zF@brb(hai}A}icxII#)T)+rZ zG{N+x0#Fgu^q1MZOoAU6-dtn6pdt8yae~Z_DU2f3Obl1VE@*6MW@@MvDQJ-F5oHjW z?8&UkEK(gL2m=+oeoB($%V|nSsEG`7yz9&cC!Eg delta 87 zcmV-d0I2`q3hoGyc@QxJ217_mOhhsR15{5?4FQu60&5Hn02FHh0Du{&6rcu?!Sn%c tk&P@C4FVDH0FWHGlmQX&0qoWR&?ppp0RW>R0|5gRIRcaF0yLvw1OYGP7_e-eO;r}PU}WlWIKkL6 ziE%Hhr~!yG!Su^aMhNo-`xn2-J&f+$qMCV(Ob2*=aNUrde1b7JJfD&2f+JAYbV(t| zgdfTql(q=`5d^Y1Zpa=~{h@q7b%M+fS0b=J5{><#0UUu^+0C; delta 50 zcmX@XzL9N1598!BjP8>)nLNbv7?}?6{NS1(b3;~C1H}FydSezN)8vaxUnf6c;$UK6 F006v35Xb-k diff --git a/src/plugins/tools/netx/testdata/hlp4.oas b/src/plugins/tools/netx/testdata/hlp4.oas index 508689d78384331dd520173a06f620bce6f41563..ea722bf014d066b17a015c661a82eeccf4c9d86c 100644 GIT binary patch delta 246 zcmdnTc7uIFr?{wfJ_94u1;-ccZyc5siqtYSNcM;_Fl?Nt#^@kwna{{|BVz*dMx`5O zD@0bf&2(PrwnO};*_63VQ5+dc1GWN zQO!I?rUN`bxNgXbYJgZDL~qPu1R2frLwSSJ7J)y4K$AFb$R1Svp?pAfg3J%)PKSvy zUrd)2irO$Tt#F&byksZSU2X=5eLs}Hm;!YGWj^R_;OzO#bcdS(=;|wC7c@3BGchoT klzb4~A>MG5i9w`hhxiB4$qGztj7*aqnSQf0G%zp#0B5CB00000 delta 64 zcmcb?zK?A}Cx=Kn6GMY!kLbn)YK)WFn0zO1V^We7wajB=I>7UTYl6%TSy2rT`-A9> TS&U4RA2NNN{DFysiGcwChOrb7 diff --git a/src/plugins/tools/netx/testdata/hlp5.oas b/src/plugins/tools/netx/testdata/hlp5.oas index dd9ffd9e8730124de87f8f13003edee842f41cb2..04963b4004a1fbe601de1936ebd4f499d832c79f 100644 GIT binary patch delta 263 zcmZ3%{*`0HWJU*3%X~(z8yORrH!9sQTOqQ-ZKm@|w;kd)&8Ey{nyM^n!N}C%aDuUC z660Q0Q3DWXg6Wr;j1cAv_Ah>uCo{Tpi)!XEG9BRg!F5A+@_ELf@O(z53yv??-#9EO z1ex$dd4tj#=|6%%HpdOwgQ`E252#L%`JvqDFi{4q#)gq;h1&$?IY*f8ax*~e{Gt5D z0jL8g^FeO|XUjjPJKPMD&6)I=MH%xZr!v`C1Kka_ ZkTE@a7gA& VkU21)iD_~p^QXxZn7Nr47yuHc7GnSa diff --git a/src/plugins/tools/netx/testdata/hlp6.oas b/src/plugins/tools/netx/testdata/hlp6.oas index 56db21f0ac53065c3451ab3f5d4bb594487ff39e..38a6ae94af595d277825cb027aed8ecd62aeea76 100644 GIT binary patch delta 378 zcmdnPJ)3VsI8(i-Wj-U*1;-ccZyc5sirO%8-N=~0yiw_f*$R;rZZn-%y6q6ZX*TBw z(_L-`Q42<To#0IAtdzNSxz_>_OEZ$_G>@$ox?5beJduHH~S7 z+XQB)ZX1w}38sGzF#Zu_5H$eteke=5aex^0g8fe^1JHh^4|*FoTmCWK;ocm^l!oNK zFQz@;7=hZbI&jHOCXil`i{`9jd@`Ado1v#t2%Ezo#sGZ+w7s3NCHu?_W{|%??gSbF zvI59^puK^!=QGok=`8M|8jNf^#9vyzu$Iz?sS+a^F$J+XoBgNnIJ_#dpjI}Zt$D@no+?-)E?PU Z45B7*S22j%AUTUcq!#Y3&0V4R%Iq`)mw4j0?JpUIFB08>d0zyJUM diff --git a/src/plugins/tools/netx/testdata/hlp8.oas b/src/plugins/tools/netx/testdata/hlp8.oas index 9b32237c9ff7f20a379f22f478157cdec583377b..3b9f1acc1dc5644b86af93ae4f54e58788895ed9 100644 GIT binary patch delta 298 zcmZqTXyckNQC-wJpMjBSh1&$?B|Dk!a*J9pGJVk7z}fSe=?=F@CDRqL3mO}mnHV=R@r$Q)4J!1;oGBj*Geko=NDpf0u-mKT9$TyQ*~`htD3B~wEv*xVn=Ka{r! n{1F7X1!4zSHKrXlNPcF3xcrCm7gL~4pbR_!7&h-@@?-=6ib`nP delta 35 rcmZqUYT}qMkz1sgiQ$Ua1&s~OOdEH|F-{g^s-OIVsbTY8W=}={*MJOD diff --git a/src/plugins/tools/netx/testdata/hlp9.oas b/src/plugins/tools/netx/testdata/hlp9.oas index 16d63a4f8704786db6ac1025095c4ea6a2ee8f30..9b597f891b983d01a388f2b1181b6eee97608c45 100644 GIT binary patch delta 221 zcmZ3%evM*S%Dmm8?pyge<&YNogni=xzk~y%o9nNq6wy7W`YzMfONcI|Kc}Up2^b! z &result) const + { + db::ShapeInteractions sized_interactions = interactions; + for (db::ShapeInteractions::iterator i = sized_interactions.begin (); i != sized_interactions.end (); ++i) { + + const db::PolygonRef &ref = interactions.shape (i->first); + db::Polygon poly = ref.obj ().transformed (ref.trans ()); + poly.size (m_dist / 2, m_dist / 2); + sized_interactions.add_shape (i->first, db::PolygonRef (poly, layout->shape_repository ())); + + for (db::ShapeInteractions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + const db::PolygonRef &ref = interactions.shape (*j); + db::Polygon poly = ref.obj ().transformed (ref.trans ()); + poly.size (m_dist / 2, m_dist / 2); + sized_interactions.add_shape (*j, db::PolygonRef (poly, layout->shape_repository ())); + } + + } + + SelfOverlapMergeLocalOperation::compute_local (layout, sized_interactions, result); + } + + db::Coord dist () const + { + return m_dist; + } + +private: + db::Coord m_dist; +}; + /** * @brief Turns a layer into polygons and polygon references * The hierarchical processor needs polygon references and can't work on polygons directly. @@ -228,8 +272,12 @@ void run_test_bool_with_size_gen (tl::TestBase *_this, const char *file, TestMod p.layer = 2; p.datatype = 0; - lmap.map (db::LDPair (2, 0), l2 = layout_org.insert_layer ()); - layout_org.set_properties (l2, p); + if (mode == TMSelfOverlap) { + lmap.map (db::LDPair (2, 0), l2 = l1); + } else { + lmap.map (db::LDPair (2, 0), l2 = layout_org.insert_layer ()); + layout_org.set_properties (l2, p); + } p.layer = out_layer_num; p.datatype = 0; @@ -246,19 +294,27 @@ void run_test_bool_with_size_gen (tl::TestBase *_this, const char *file, TestMod normalize_layer (layout_org, l1); normalize_layer (layout_org, l2); - BoolAndOrNotWithSizedLocalOperation op (mode == TMAnd, dist); + db::LocalOperation *lop = 0; + BoolAndOrNotWithSizedLocalOperation bool_op (mode == TMAnd, dist); + SelfOverlapWithSizedLocalOperation self_intersect_op (2, dist); + if (mode == TMSelfOverlap) { + lop = &self_intersect_op; + } else { + lop = &bool_op; + } + if (single) { db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); if (! context_doc) { - proc.run (&op, l1, l2, lout); + proc.run (lop, l1, l2, lout); } else { db::LocalProcessorContexts contexts; - proc.compute_contexts (contexts, &op, l1, l2); + proc.compute_contexts (contexts, lop, l1, l2); *context_doc = contexts_to_s (&layout_org, contexts); - proc.compute_results (contexts, &op, lout); + proc.compute_results (contexts, lop, lout); } } else { @@ -268,12 +324,12 @@ void run_test_bool_with_size_gen (tl::TestBase *_this, const char *file, TestMod db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &layout_org2, &layout_org2.cell (*layout_org2.begin_top_down ())); if (! context_doc) { - proc.run (&op, l1, l2, lout); + proc.run (lop, l1, l2, lout); } else { db::LocalProcessorContexts contexts; - proc.compute_contexts (contexts, &op, l1, l2); + proc.compute_contexts (contexts, lop, l1, l2); *context_doc = contexts_to_s (&layout_org, contexts); - proc.compute_results (contexts, &op, lout); + proc.compute_results (contexts, lop, lout); } } @@ -853,55 +909,55 @@ TEST(TwoInputsNotWithSize10) TEST(BasicSelfOverlap1) { - // Simple flat AND + // Simple flat Self overlap run_test_bool (_this, "hlp1.oas", TMSelfOverlap, 110); } TEST(BasicSelfOverlap2) { - // Up/down and down/up interactions, AND + // Up/down and down/up interactions, Self overlap run_test_bool (_this, "hlp2.oas", TMSelfOverlap, 110); } TEST(BasicSelfOverlap3) { - // Variant building, AND + // Variant building, Self overlap run_test_bool (_this, "hlp3.oas", TMSelfOverlap, 110); } TEST(BasicSelfOverlap4) { - // Sibling interactions, variant building, AND + // Sibling interactions, variant building, Self overlap run_test_bool (_this, "hlp4.oas", TMSelfOverlap, 110); } TEST(BasicSelfOverlap5) { - // Variant building with intermediate hierarchy, AND + // Variant building with intermediate hierarchy, Self overlap run_test_bool (_this, "hlp5.oas", TMSelfOverlap, 110); } TEST(BasicSelfOverlap6) { - // Extreme variants (copy, vanishing), AND + // Extreme variants (copy, vanishing), Self overlap run_test_bool (_this, "hlp6.oas", TMSelfOverlap, 110); } TEST(BasicSelfOverlap7) { - // Context replication - direct and indirect, AND + // Context replication - direct and indirect, Self overlap run_test_bool (_this, "hlp7.oas", TMSelfOverlap, 110); } TEST(BasicSelfOverlap8) { - // Mixed sibling-parent contexts, AND + // Mixed sibling-parent contexts, Self overlap run_test_bool (_this, "hlp8.oas", TMSelfOverlap, 110); } TEST(BasicSelfOverlap9) { - // Top-level ring structure, AND + // Top-level ring structure, Self overlap std::string doc; run_test_bool (_this, "hlp9.oas", TMSelfOverlap, 110, &doc); EXPECT_EQ (doc, @@ -917,62 +973,61 @@ TEST(BasicSelfOverlap9) TEST(BasicSelfOverlap10) { - // Array instances, AND + // Array instances, Self overlap run_test_bool (_this, "hlp10.oas", TMSelfOverlap, 110); } -#if 0 // @@@ TEST(BasicSelfOverlapWithSize1) { - // Simple flat AND + // Simple flat Self overlap run_test_bool_with_size (_this, "hlp1.oas", TMSelfOverlap, 1500, 111); } TEST(BasicSelfOverlapWithSize2) { - // Up/down and down/up interactions, AND + // Up/down and down/up interactions, Self overlap run_test_bool_with_size (_this, "hlp2.oas", TMSelfOverlap, 1500, 111); } TEST(BasicSelfOverlapWithSize3) { - // Variant building, AND + // Variant building, Self overlap run_test_bool_with_size (_this, "hlp3.oas", TMSelfOverlap, 1500, 111); } TEST(BasicSelfOverlapWithSize4) { - // Sibling interactions, variant building, AND + // Sibling interactions, variant building, Self overlap run_test_bool_with_size (_this, "hlp4.oas", TMSelfOverlap, 1500, 111); } TEST(BasicSelfOverlapWithSize5) { - // Variant building with intermediate hierarchy, AND + // Variant building with intermediate hierarchy, Self overlap run_test_bool_with_size (_this, "hlp5.oas", TMSelfOverlap, 1500, 111); } TEST(BasicSelfOverlapWithSize6) { - // Extreme variants (copy, vanishing), AND + // Extreme variants (copy, vanishing), Self overlap run_test_bool_with_size (_this, "hlp6.oas", TMSelfOverlap, 1500, 111); } TEST(BasicSelfOverlapWithSize7) { - // Context replication - direct and indirect, AND + // Context replication - direct and indirect, Self overlap run_test_bool_with_size (_this, "hlp7.oas", TMSelfOverlap, 1500, 111); } TEST(BasicSelfOverlapWithSize8) { - // Mixed sibling-parent contexts, AND + // Mixed sibling-parent contexts, Self overlap run_test_bool_with_size (_this, "hlp8.oas", TMSelfOverlap, 1500, 111); } TEST(BasicSelfOverlapWithSize9) { - // Top-level ring structure, AND + // Top-level ring structure, Self overlap std::string doc; run_test_bool_with_size (_this, "hlp9.oas", TMSelfOverlap, 1500, 111, &doc); EXPECT_EQ (doc, @@ -981,16 +1036,14 @@ TEST(BasicSelfOverlapWithSize9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 1 shapes (1 times)\n" "CHILD1[1] 0 insts, 6 shapes (2 times)\n" ); } TEST(BasicSelfOverlapWithSize10) { - // Array instances, AND + // Array instances, Self overlap run_test_bool_with_size (_this, "hlp10.oas", TMSelfOverlap, 150, 111); } -#endif - From 0bb45dd092f876d00badb7899540135757d1e07c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Oct 2018 23:59:47 +0200 Subject: [PATCH 029/335] WIP: some refactoring. --- .../tools/netx/db_plugin/dbHierProcessor.cc | 255 +++++------------- .../tools/netx/db_plugin/dbHierProcessor.h | 45 +--- .../tools/netx/db_plugin/dbLocalOperation.cc | 203 ++++++++++++++ .../tools/netx/db_plugin/dbLocalOperation.h | 148 ++++++++++ .../tools/netx/db_plugin/db_plugin.pro | 2 + 5 files changed, 416 insertions(+), 237 deletions(-) create mode 100644 src/plugins/tools/netx/db_plugin/dbLocalOperation.cc create mode 100644 src/plugins/tools/netx/db_plugin/dbLocalOperation.h diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 0828d72f0..14fe6f8a0 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -34,171 +34,38 @@ namespace db { - // --------------------------------------------------------------------------------------------- -// BoolAndOrNotLocalOperation implementation +// Shape reference translator -namespace { - -class PolygonRefGenerator - : public PolygonSink +template +class shape_reference_translator { public: - /** - * @brief Constructor specifying an external vector for storing the polygons - */ - PolygonRefGenerator (db::Layout *layout, std::set &polyrefs) - : PolygonSink (), mp_layout (layout), mp_polyrefs (&polyrefs) - { } + typedef typename Ref::shape_type shape_type; - /** - * @brief Implementation of the PolygonSink interface - */ - virtual void put (const db::Polygon &polygon) + shape_reference_translator (db::Layout *target_layout) + : mp_layout (target_layout) { - mp_polyrefs->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); + // .. nothing yet .. + } + + Ref operator() (const Ref &ref) const + { + shape_type sh = ref.obj ().transformed (ref.trans ()); + return Ref (sh, mp_layout->shape_repository ()); + } + + template + Ref operator() (const Ref &ref, const Trans &tr) const + { + shape_type sh = ref.obj ().transformed (tr * Trans (ref.trans ())); + return Ref (sh, mp_layout->shape_repository ()); } private: db::Layout *mp_layout; - std::set *mp_polyrefs; }; -} - -BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and) - : m_is_and (is_and) -{ - // .. nothing yet .. -} - -// --------------------------------------------------------------------------------------------- - -LocalOperation::on_empty_intruder_mode -BoolAndOrNotLocalOperation::on_empty_intruder_hint () const -{ - return m_is_and ? LocalOperation::Drop : LocalOperation::Copy; -} - -std::string -BoolAndOrNotLocalOperation::description () const -{ - return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); -} - -void -BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const -{ - db::EdgeProcessor ep; - - size_t p1 = 0, p2 = 1; - - std::set others; - for (ShapeInteractions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (ShapeInteractions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.shape (*j)); - } - } - - for (ShapeInteractions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - const db::PolygonRef &subject = interactions.shape (i->first); - if (others.find (subject) != others.end ()) { - if (m_is_and) { - result.insert (subject); - } - } else if (i->second.empty ()) { - // shortcut (not: keep, and: drop) - if (! m_is_and) { - result.insert (subject); - } - } else { - for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - } - - if (! others.empty () || p1 > 0) { - - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - - db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); - db::PolygonRefGenerator pr (layout, result); - db::PolygonGenerator pg (pr, true, true); - ep.process (pg, op); - - } -} - -// --------------------------------------------------------------------------------------------- - -SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wrap_count) - : m_wrap_count (wrap_count) -{ - // .. nothing yet .. -} - -void SelfOverlapMergeLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const -{ - if (m_wrap_count == 0) { - return; - } - - db::EdgeProcessor ep; - - size_t p1 = 0, p2 = 1; - std::set seen; - - for (ShapeInteractions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - if (seen.find (i->first) == seen.end ()) { - seen.insert (i->first); - const db::PolygonRef &subject = interactions.shape (i->first); - for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - for (db::ShapeInteractions::iterator2 o = i->second.begin (); o != i->second.end (); ++o) { - // don't take the same (really the same, not an identical one) shape twice - the interaction - // set does not take care to list just one copy of the same item on the intruder side. - if (seen.find (*o) == seen.end ()) { - seen.insert (*o); - const db::PolygonRef &intruder = interactions.shape (*o); - for (db::PolygonRef::polygon_edge_iterator e = intruder.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - } - - } - - db::MergeOp op (m_wrap_count - 1); - db::PolygonRefGenerator pr (layout, result); - db::PolygonGenerator pg (pr, true, true); - ep.process (pg, op); -} - -SelfOverlapMergeLocalOperation::on_empty_intruder_mode SelfOverlapMergeLocalOperation::on_empty_intruder_hint () const -{ - return m_wrap_count > 1 ? LocalOperation::Drop : LocalOperation::Copy; -} - -std::string SelfOverlapMergeLocalOperation::description () const -{ - return tl::sprintf (tl::to_string (tr ("Self-overlap (wrap count %d)")), int (m_wrap_count)); -} - // --------------------------------------------------------------------------------------------- // LocalProcessorCellContext implementation @@ -226,10 +93,9 @@ LocalProcessorCellContext::propagate (const std::set &res) tl_assert (d->parent != 0); db::Layout *subject_layout = d->parent->layout (); - + shape_reference_translator rt (subject_layout); for (std::set::const_iterator r = res.begin (); r != res.end (); ++r) { - db::Polygon poly = r->obj ().transformed (d->cell_inst * db::ICplxTrans (r->trans ())); - d->parent_context->propagated ().insert (db::PolygonRef (poly, subject_layout->shape_repository ())); + d->parent_context->propagated ().insert (rt (*r, d->cell_inst)); } } @@ -411,8 +277,8 @@ public: // In order to guarantee the refs come from the subject layout, we'd need to // rewrite them to the subject layout if required. if (!mp_result->has_shape_id (id2)) { - db::Polygon poly = ref2->obj ().transformed (ref2->trans ()); - mp_result->add_shape (id2, db::PolygonRef (poly, mp_layout->shape_repository ())); + db::shape_reference_translator rt (mp_layout); + mp_result->add_shape (id2, rt (*ref2)); } } else { mp_result->add_shape (id2, *ref2); @@ -463,42 +329,14 @@ public: db::box_convert inst_bc (*mp_intruder_layout, m_intruder_layer); mp_result->add_shape (id1, *ref); + // Find all instance array members that potentially interact with the shape and use + // add_shapes_from_intruder_inst on them for (db::CellInstArray::iterator n = inst->begin_touching (ref->box ().enlarged (db::Vector (m_dist - 1, m_dist - 1)), inst_bc); !n.at_end (); ++n) { - db::ICplxTrans tn = inst->complex_trans (*n); - db::Box region = ref->box ().transformed (tn.inverted ()).enlarged (db::Vector (m_dist, m_dist)) & intruder_cell.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist)); if (! region.empty ()) { - - // @@@ TODO: should be lighter, cache, handle arrays .. - db::RecursiveShapeIterator si (*mp_intruder_layout, intruder_cell, m_intruder_layer, region); - si.shape_flags (polygon_ref_flags ()); - while (! si.at_end ()) { - - const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); - - // reuse the same id for shapes from the same instance -> this avoid duplicates with different IDs on - // the intruder side. - std::map, unsigned int>::const_iterator k = m_inst_shape_ids.find (std::make_pair (inst_id, ref2)); - if (k == m_inst_shape_ids.end ()) { - - k = m_inst_shape_ids.insert (std::make_pair (std::make_pair (inst_id, ref2), mp_result->next_id ())).first; - - db::Polygon poly = ref2->obj ().transformed (tn * si.trans () * db::ICplxTrans (ref2->trans ())); - // NOTE: we intentionally rewrite to the subject layout - this way polygon refs in the context come from the - // subject, not from the intruder. - mp_result->add_shape (k->second, db::PolygonRef (poly, mp_subject_layout->shape_repository())); - - } - - mp_result->add_interaction (id1, k->second); - - ++si; - - } - + add_shapes_from_intruder_inst (id1, intruder_cell, tn, inst_id, region); } - } } @@ -509,6 +347,39 @@ private: db::Coord m_dist; ShapeInteractions *mp_result; std::map, unsigned int> m_inst_shape_ids; + + void add_shapes_from_intruder_inst (unsigned int id1, const db::Cell &intruder_cell, const db::ICplxTrans &tn, unsigned int inst_id, const db::Box ®ion) + { + db::shape_reference_translator rt (mp_subject_layout); + + // Look up all shapes from the intruder instance which interact with the subject shape + // (given through region) + // @@@ TODO: should be lighter, cache, handle arrays .. + db::RecursiveShapeIterator si (*mp_intruder_layout, intruder_cell, m_intruder_layer, region); + si.shape_flags (polygon_ref_flags ()); + while (! si.at_end ()) { + + const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); + + // reuse the same id for shapes from the same instance -> this avoid duplicates with different IDs on + // the intruder side. + std::map, unsigned int>::const_iterator k = m_inst_shape_ids.find (std::make_pair (inst_id, ref2)); + if (k == m_inst_shape_ids.end ()) { + + k = m_inst_shape_ids.insert (std::make_pair (std::make_pair (inst_id, ref2), mp_result->next_id ())).first; + + // NOTE: we intentionally rewrite to the *subject* layout - this way polygon refs in the context come from the + // subject, not from the intruder. + mp_result->add_shape (k->second, rt (*ref2, tn * si.trans ())); + + } + + mp_result->add_interaction (id1, k->second); + + ++si; + + } + } }; static bool @@ -842,6 +713,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, for (std::map::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { db::Cell &subject_child_cell = mp_subject_layout->cell (i->first->object ().cell_index ()); + db::shape_reference_translator rt (mp_subject_layout); for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) { @@ -856,10 +728,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, // @@@ transformation of polygon refs - can this be done more efficiently? for (std::set::const_iterator p = i->second.second.begin (); p != i->second.second.end (); ++p) { if (nbox.overlaps (p->box ())) { - db::Polygon poly = p->obj ().transformed (tni * db::ICplxTrans (p->trans ())); - // NOTE: we intentionally transform into the *subject* layout so the intruders are local to - // the subject layout. - intruders_below.second.insert (db::PolygonRef (poly, mp_subject_layout->shape_repository ())); + intruders_below.second.insert (rt (*p, tni)); } } diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index 4cceeac97..f99235418 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -27,6 +27,7 @@ #include "dbLayout.h" #include "dbPluginCommon.h" +#include "dbLocalOperation.h" #include #include @@ -77,50 +78,6 @@ private: unsigned int m_id; }; -class DB_PLUGIN_PUBLIC LocalOperation -{ -public: - enum on_empty_intruder_mode { - Ignore = 0, Copy, Drop - }; - - LocalOperation () { } - virtual ~LocalOperation () { } - - virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const = 0; - virtual on_empty_intruder_mode on_empty_intruder_hint () const = 0; - virtual std::string description () const = 0; - virtual db::Coord dist () const { return 0; } -}; - -class DB_PLUGIN_PUBLIC BoolAndOrNotLocalOperation - : public LocalOperation -{ -public: - BoolAndOrNotLocalOperation (bool is_and); - - virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const; - virtual on_empty_intruder_mode on_empty_intruder_hint () const; - virtual std::string description () const; - -private: - bool m_is_and; -}; - -class DB_PLUGIN_PUBLIC SelfOverlapMergeLocalOperation - : public LocalOperation -{ -public: - SelfOverlapMergeLocalOperation (unsigned int wrap_count); - - virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const; - virtual on_empty_intruder_mode on_empty_intruder_hint () const; - virtual std::string description () const; - -private: - unsigned int m_wrap_count; -}; - // @@@ TODO: should be hidden (private data?) struct DB_PLUGIN_PUBLIC LocalProcessorCellDrop { diff --git a/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc b/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc new file mode 100644 index 000000000..2ee0846b8 --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc @@ -0,0 +1,203 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbHierProcessor.h" +#include "dbBoxScanner.h" +#include "dbRecursiveShapeIterator.h" +#include "dbBoxConvert.h" +#include "dbEdgeProcessor.h" +#include "dbPolygonGenerators.h" +#include "tlLog.h" +#include "tlTimer.h" +#include "tlInternational.h" + +namespace db +{ + + +// --------------------------------------------------------------------------------------------- +// BoolAndOrNotLocalOperation implementation + +namespace { + +class PolygonRefGenerator + : public PolygonSink +{ +public: + /** + * @brief Constructor specifying an external vector for storing the polygons + */ + PolygonRefGenerator (db::Layout *layout, std::set &polyrefs) + : PolygonSink (), mp_layout (layout), mp_polyrefs (&polyrefs) + { } + + /** + * @brief Implementation of the PolygonSink interface + */ + virtual void put (const db::Polygon &polygon) + { + mp_polyrefs->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); + } + +private: + db::Layout *mp_layout; + std::set *mp_polyrefs; +}; + +} + +// --------------------------------------------------------------------------------------------- + +BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and) + : m_is_and (is_and) +{ + // .. nothing yet .. +} + +LocalOperation::on_empty_intruder_mode +BoolAndOrNotLocalOperation::on_empty_intruder_hint () const +{ + return m_is_and ? LocalOperation::Drop : LocalOperation::Copy; +} + +std::string +BoolAndOrNotLocalOperation::description () const +{ + return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); +} + +void +BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const +{ + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + + std::set others; + for (ShapeInteractions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (ShapeInteractions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.shape (*j)); + } + } + + for (ShapeInteractions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::PolygonRef &subject = interactions.shape (i->first); + if (others.find (subject) != others.end ()) { + if (m_is_and) { + result.insert (subject); + } + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + if (! m_is_and) { + result.insert (subject); + } + } else { + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + } + + if (! others.empty () || p1 > 0) { + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); + db::PolygonRefGenerator pr (layout, result); + db::PolygonGenerator pg (pr, true, true); + ep.process (pg, op); + + } +} + +// --------------------------------------------------------------------------------------------- + +SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wrap_count) + : m_wrap_count (wrap_count) +{ + // .. nothing yet .. +} + +void SelfOverlapMergeLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const +{ + if (m_wrap_count == 0) { + return; + } + + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + std::set seen; + + for (ShapeInteractions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + if (seen.find (i->first) == seen.end ()) { + seen.insert (i->first); + const db::PolygonRef &subject = interactions.shape (i->first); + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + for (db::ShapeInteractions::iterator2 o = i->second.begin (); o != i->second.end (); ++o) { + // don't take the same (really the same, not an identical one) shape twice - the interaction + // set does not take care to list just one copy of the same item on the intruder side. + if (seen.find (*o) == seen.end ()) { + seen.insert (*o); + const db::PolygonRef &intruder = interactions.shape (*o); + for (db::PolygonRef::polygon_edge_iterator e = intruder.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + } + + } + + db::MergeOp op (m_wrap_count - 1); + db::PolygonRefGenerator pr (layout, result); + db::PolygonGenerator pg (pr, true, true); + ep.process (pg, op); +} + +SelfOverlapMergeLocalOperation::on_empty_intruder_mode SelfOverlapMergeLocalOperation::on_empty_intruder_hint () const +{ + return m_wrap_count > 1 ? LocalOperation::Drop : LocalOperation::Copy; +} + +std::string SelfOverlapMergeLocalOperation::description () const +{ + return tl::sprintf (tl::to_string (tr ("Self-overlap (wrap count %d)")), int (m_wrap_count)); +} + +} + diff --git a/src/plugins/tools/netx/db_plugin/dbLocalOperation.h b/src/plugins/tools/netx/db_plugin/dbLocalOperation.h new file mode 100644 index 000000000..6f726dd24 --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/dbLocalOperation.h @@ -0,0 +1,148 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbLocalOperation +#define HDR_dbLocalOperation + +#include "dbLayout.h" +#include "dbPluginCommon.h" + +#include +#include +#include + +namespace db +{ + +class ShapeInteractions; + +/** + * @brief A base class for "local operations" + * A local operation is any operation whose result can be computed by + * combining the results derived from individual shape pairs. + * The shape pairs can originate from different or the same layer. + * If the layers are different, one layer is the subject layer, the + * other layer is the "intruder" layer. Subject shapes are always + * considered, intruder shapes only if they interact with subject shapes. + * This class implements the actual operation. It receives a + * cluster of subject shapes vs. corresponding intruder shapes. + */ +class DB_PLUGIN_PUBLIC LocalOperation +{ +public: + /** + * @brief Indicates the desired behaviour for subject shapes for which there is no intruder + */ + enum on_empty_intruder_mode { + /** + * @brief Don't imply a specific behaviour + */ + Ignore = 0, + + /** + * @brief Copy the subject shape + */ + Copy, + + /** + * @brief Drop the subject shape + */ + Drop + }; + + /** + * @brief Constructor + */ + LocalOperation () { } + + /** + * @brief Destructor + */ + virtual ~LocalOperation () { } + + /** + * @brief Computes the results from a given set of interacting shapes + * @param layout The layout to which the shapes belong + * @param interactions The interaction set + * @param result The container to which the results are written + */ + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const = 0; + + /** + * @brief Indicates the desired behaviour when a shape does not have an intruder + */ + virtual on_empty_intruder_mode on_empty_intruder_hint () const { return Ignore; } + + /** + * @brief Gets a description text for this operation + */ + virtual std::string description () const = 0; + + /** + * @brief Gets the interaction distance + * A distance of means the shapes must overlap in order to interact. + */ + virtual db::Coord dist () const { return 0; } +}; + +/** + * @brief Implements a boolean AND or NOT operation + */ +class DB_PLUGIN_PUBLIC BoolAndOrNotLocalOperation + : public LocalOperation +{ +public: + BoolAndOrNotLocalOperation (bool is_and); + + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const; + virtual on_empty_intruder_mode on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + bool m_is_and; +}; + +/** + * @brief Implements a merge operation with an overlap count + * With a given wrap_count, the result will only contains shapes where + * the original shapes overlap at least "wrap_count" times. + */ +class DB_PLUGIN_PUBLIC SelfOverlapMergeLocalOperation + : public LocalOperation +{ +public: + SelfOverlapMergeLocalOperation (unsigned int wrap_count); + + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const; + virtual on_empty_intruder_mode on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + unsigned int m_wrap_count; +}; + +} + +#endif + diff --git a/src/plugins/tools/netx/db_plugin/db_plugin.pro b/src/plugins/tools/netx/db_plugin/db_plugin.pro index 089fef33e..333cf6c44 100644 --- a/src/plugins/tools/netx/db_plugin/db_plugin.pro +++ b/src/plugins/tools/netx/db_plugin/db_plugin.pro @@ -7,10 +7,12 @@ include($$PWD/../../../db_plugin.pri) HEADERS = \ dbNetExtractor.h \ dbHierProcessor.h \ + dbLocalOperation.h SOURCES = \ dbNetExtractor.cc \ dbHierProcessor.cc \ dbNetExtractorPlugin.cc \ gsiDeclDbNetExtractor.cc \ + dbLocalOperation.cc From 35e03c5f95643913145aee39d7ada67896228988 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 Oct 2018 16:56:14 +0200 Subject: [PATCH 030/335] Added one more testcase. --- .../tools/netx/db_plugin/dbHierProcessor.cc | 7 +- src/plugins/tools/netx/testdata/hlp11.oas | Bin 0 -> 823 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 138 +++++------------- 3 files changed, 42 insertions(+), 103 deletions(-) create mode 100644 src/plugins/tools/netx/testdata/hlp11.oas diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 14fe6f8a0..806a3c8fd 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -354,7 +354,7 @@ private: // Look up all shapes from the intruder instance which interact with the subject shape // (given through region) - // @@@ TODO: should be lighter, cache, handle arrays .. + // TODO: should be lighter, cache, handle arrays .. db::RecursiveShapeIterator si (*mp_intruder_layout, intruder_cell, m_intruder_layer, region); si.shape_flags (polygon_ref_flags ()); while (! si.at_end ()) { @@ -401,7 +401,7 @@ instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, u if (! ibox1.empty ()) { - // @@@ TODO: in some cases, it may be possible to optimize this for arrays + // TODO: in some cases, it may be possible to optimize this for arrays for (db::CellInstArray::iterator k = inst2->begin_touching (ibox1.enlarged (db::Vector (-1, -1)), inst2_bc); ! k.at_end (); ++k) { @@ -725,14 +725,13 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, std::pair, std::set > intruders_below; - // @@@ transformation of polygon refs - can this be done more efficiently? for (std::set::const_iterator p = i->second.second.begin (); p != i->second.second.end (); ++p) { if (nbox.overlaps (p->box ())) { intruders_below.second.insert (rt (*p, tni)); } } - // @@@ TODO: in some cases, it may be possible to optimize this for arrays + // TODO: in some cases, it may be possible to optimize this for arrays for (std::set::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) { for (db::CellInstArray::iterator k = (*j)->begin_touching (nbox.enlarged (db::Vector (-1, -1)), inst_bcii); ! k.at_end (); ++k) { diff --git a/src/plugins/tools/netx/testdata/hlp11.oas b/src/plugins/tools/netx/testdata/hlp11.oas new file mode 100644 index 0000000000000000000000000000000000000000..df75656981e7595538ed2b7f85ce2e77581cd18d GIT binary patch literal 823 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4Y|b8@J}yR3+K?F}$jc~_&B)Lo zxk6+Cn@Gh6(HVjtRxs@l7pd4G{z3G{BPIrsnk!-}L@q=vVB=;uqbMM1n9j&_LF0hx zjwDeFMy4Aq9efNkv_Q-sTpN^tdOzs#GKf?&GF%b6pm9X!qJ~HzQ-frW=-X?IA_X5r zcZg5PgP8qiIpa)0k%~8bZ`6B5fyx3N2+f_!2r`~!LEsAApIj4}kLZXRW-u}>V7npP zD=KQh$TCCg2Fp+76}mvffLx%&0acJ5AajTK0{JC{yi9^07~WiCyr3cYfpLP&jwy^F zmw-GsLFR@m#Cr`#fld^u06A|%GgCt?#5FJefjkZN>VvWyvS6=@RDxa308|5ZIZ!7; zG0Tbxw0%w8@Py!k{Km%5|0Xc9Ff*k<&BhV;hPcp!~ T2s8ld!wDytm>NbhU|;|Ma}qSH literal 0 HcmV?d00001 diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index 6dd1c5b03..db783ecff 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -37,7 +37,9 @@ enum TestMode { TMAnd = 0, TMNot = 1, - TMSelfOverlap = 2 + TMAndSwapped = 2, + TMNotSwapped = 3, + TMSelfOverlap = 4 }; /** @@ -156,12 +158,13 @@ std::string contexts_to_s (db::Layout *layout, db::LocalProcessorContexts &conte return res; } -void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc, bool single) +void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc, bool single, db::Coord dist) { db::Layout layout_org; unsigned int l1 = 0, l2 = 0, lout = 0; db::LayerMap lmap; + bool swap = (mode == TMAndSwapped || mode == TMNotSwapped); { tl::InputStream stream (testdata (file)); @@ -169,17 +172,17 @@ void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, in db::LayerProperties p; - p.layer = 1; + p.layer = swap ? 2 : 1; p.datatype = 0; - lmap.map (db::LDPair (1, 0), l1 = layout_org.insert_layer ()); + lmap.map (db::LDPair (p.layer, p.datatype), l1 = layout_org.insert_layer ()); layout_org.set_properties (l1, p); - p.layer = 2; + p.layer = swap ? 1 : 2; p.datatype = 0; if (mode == TMSelfOverlap) { - lmap.map (db::LDPair (2, 0), l2 = l1); + lmap.map (db::LDPair (p.layer, p.datatype), l2 = l1); } else { - lmap.map (db::LDPair (2, 0), l2 = layout_org.insert_layer ()); + lmap.map (db::LDPair (p.layer, p.datatype), l2 = layout_org.insert_layer ()); layout_org.set_properties (l2, p); } @@ -201,12 +204,22 @@ void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, in } db::LocalOperation *lop = 0; - db::BoolAndOrNotLocalOperation bool_op (mode == TMAnd); + db::BoolAndOrNotLocalOperation bool_op (mode == TMAnd || mode == TMAndSwapped); db::SelfOverlapMergeLocalOperation self_intersect_op (2); + BoolAndOrNotWithSizedLocalOperation sized_bool_op (mode == TMAnd || mode == TMAndSwapped, dist); + SelfOverlapWithSizedLocalOperation sized_self_intersect_op (2, dist); if (mode == TMSelfOverlap) { - lop = &self_intersect_op; + if (dist > 0) { + lop = &sized_self_intersect_op; + } else { + lop = &self_intersect_op; + } } else { - lop = &bool_op; + if (dist > 0) { + lop = &sized_bool_op; + } else { + lop = &bool_op; + } } if (single) { @@ -244,107 +257,22 @@ void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, in void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0) { - run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true); + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true, 0); } void run_test_bool2 (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0) { - run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false); -} - -void run_test_bool_with_size_gen (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc, bool single) -{ - db::Layout layout_org; - - unsigned int l1 = 0, l2 = 0, lout = 0; - db::LayerMap lmap; - - { - tl::InputStream stream (testdata (file)); - db::Reader reader (stream); - - db::LayerProperties p; - - p.layer = 1; - p.datatype = 0; - lmap.map (db::LDPair (1, 0), l1 = layout_org.insert_layer ()); - layout_org.set_properties (l1, p); - - p.layer = 2; - p.datatype = 0; - if (mode == TMSelfOverlap) { - lmap.map (db::LDPair (2, 0), l2 = l1); - } else { - lmap.map (db::LDPair (2, 0), l2 = layout_org.insert_layer ()); - layout_org.set_properties (l2, p); - } - - p.layer = out_layer_num; - p.datatype = 0; - lmap.map (db::LDPair (out_layer_num, 0), lout = layout_org.insert_layer ()); - layout_org.set_properties (lout, p); - - db::LoadLayoutOptions options; - options.get_options ().layer_map = lmap; - options.get_options ().create_other_layers = false; - reader.read (layout_org, options); - } - - layout_org.clear_layer (lout); - normalize_layer (layout_org, l1); - normalize_layer (layout_org, l2); - - db::LocalOperation *lop = 0; - BoolAndOrNotWithSizedLocalOperation bool_op (mode == TMAnd, dist); - SelfOverlapWithSizedLocalOperation self_intersect_op (2, dist); - if (mode == TMSelfOverlap) { - lop = &self_intersect_op; - } else { - lop = &bool_op; - } - - - if (single) { - - db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); - - if (! context_doc) { - proc.run (lop, l1, l2, lout); - } else { - db::LocalProcessorContexts contexts; - proc.compute_contexts (contexts, lop, l1, l2); - *context_doc = contexts_to_s (&layout_org, contexts); - proc.compute_results (contexts, lop, lout); - } - - } else { - - db::Layout layout_org2 = layout_org; - - db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &layout_org2, &layout_org2.cell (*layout_org2.begin_top_down ())); - - if (! context_doc) { - proc.run (lop, l1, l2, lout); - } else { - db::LocalProcessorContexts contexts; - proc.compute_contexts (contexts, lop, l1, l2); - *context_doc = contexts_to_s (&layout_org, contexts); - proc.compute_results (contexts, lop, lout); - } - - } - - db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false, 0); } void run_test_bool_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0) { - run_test_bool_with_size_gen (_this, file, mode, dist, out_layer_num, context_doc, true); + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true, dist); } void run_test_bool2_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0) { - run_test_bool_with_size_gen (_this, file, mode, dist, out_layer_num, context_doc, false); + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false, dist); } TEST(BasicAnd1) @@ -627,6 +555,18 @@ TEST(BasicNotWithSize10) run_test_bool_with_size (_this, "hlp10.oas", TMNot, 150, 103); } +TEST(BasicNotWithSize11) +{ + // Up/down and down/up interactions, NOT + run_test_bool_with_size (_this, "hlp11.oas", TMNot, 1500, 103); +} + +TEST(BasicNotWithSizeSwappedLayers11) +{ + // Up/down and down/up interactions, NOT + run_test_bool_with_size (_this, "hlp11.oas", TMNotSwapped, 1500, 104); +} + TEST(TwoInputsAnd1) { // Simple flat AND From 121ec463902d2609f540bae9d0f751804b6664dc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 8 Oct 2018 00:10:54 +0200 Subject: [PATCH 031/335] Attempt to introduce multithreading into hierarchical processor However, performance does not scale well currently. --- src/db/db/dbLayout.h | 11 + src/gsi/gsi/gsiDeclTl.cc | 8 +- .../tools/netx/db_plugin/dbHierProcessor.cc | 244 +++++++++++++++--- .../tools/netx/db_plugin/dbHierProcessor.h | 110 ++++++-- .../tools/netx/db_plugin/dbLocalOperation.cc | 1 + .../tools/netx/db_plugin/dbNetExtractor.cc | 9 +- .../tools/netx/db_plugin/dbNetExtractor.h | 7 + .../netx/db_plugin/gsiDeclDbNetExtractor.cc | 6 + .../netx/unit_tests/dbHierProcessorTests.cc | 94 ++++++- 9 files changed, 426 insertions(+), 64 deletions(-) diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index b3d832e2f..5dd824c64 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -38,6 +38,7 @@ #include "tlException.h" #include "tlVector.h" #include "tlString.h" +#include "tlThreads.h" #include "gsi.h" #include @@ -572,6 +573,15 @@ public: return m_properties_repository; } + /** + * @brief Gets the lock for the layout object + * This is a generic lock that can be used to lock modifications against multiple threads. + */ + tl::Mutex &lock () + { + return m_lock; + } + /** * @brief Collect memory statistics */ @@ -1630,6 +1640,7 @@ private: int m_waste_layer; bool m_editable; meta_info m_meta_info; + tl::Mutex m_lock; /** * @brief Sort the cells topologically diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index be1014d2b..dfcee4871 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -152,7 +152,7 @@ namespace gsi static std::string timer_to_s (const tl::Timer *timer) { - return tl::sprintf ("%.12gs (user), %.12gs (kernel)", timer->sec_user (), timer->sec_sys ()); + return tl::sprintf ("%.12gs (sys), %.12gs (user), %.12gs (wall)", timer->sec_sys (), timer->sec_user (), timer->sec_wall ()); } Class decl_Timer ("tl", "Timer", @@ -162,7 +162,11 @@ Class decl_Timer ("tl", "Timer", gsi::method ("sys", &tl::Timer::sec_sys, "@brief Returns the elapsed CPU time in kernel mode from start to stop in seconds\n" ) + - gsi::method_ext ("to_s", &timer_to_s, + gsi::method ("wall", &tl::Timer::sec_wall, + "@brief Returns the elapsed real time from start to stop in seconds\n" + "This method has been introduced in version 0.26." + ) + + gsi::method_ext ("to_s", &timer_to_s, "@brief Produces a string with the currently elapsed times\n" ) + gsi::method ("start", &tl::Timer::start, diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index 806a3c8fd..faf06d33e 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -51,6 +51,7 @@ public: Ref operator() (const Ref &ref) const { + tl::MutexLocker locker (&mp_layout->lock ()); shape_type sh = ref.obj ().transformed (ref.trans ()); return Ref (sh, mp_layout->shape_repository ()); } @@ -58,6 +59,7 @@ public: template Ref operator() (const Ref &ref, const Trans &tr) const { + tl::MutexLocker locker (&mp_layout->lock ()); shape_type sh = ref.obj ().transformed (tr * Trans (ref.trans ())); return Ref (sh, mp_layout->shape_repository ()); } @@ -131,7 +133,7 @@ LocalProcessorCellContexts::create (const key_type &intruders) } void -LocalProcessorCellContexts::compute_results (LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, LocalProcessor *proc) +LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, const LocalProcessor *proc) { bool first = true; std::set common; @@ -148,17 +150,27 @@ LocalProcessorCellContexts::compute_results (LocalProcessorContexts &contexts, d if (first) { - common = c->second.propagated (); + { + tl::MutexLocker locker (&contexts.lock ()); + common = c->second.propagated (); + } + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, common); first = false; } else { - std::set res = c->second.propagated (); + std::set res; + { + tl::MutexLocker locker (&contexts.lock ()); + res = c->second.propagated (); + } + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, res); if (common.empty ()) { + tl::MutexLocker locker (&contexts.lock ()); c->second.propagate (res); } else if (res != common) { @@ -173,6 +185,7 @@ LocalProcessorCellContexts::compute_results (LocalProcessorContexts &contexts, d common.swap (new_common); for (std::map::iterator cc = m_contexts.begin (); cc != c; ++cc) { + tl::MutexLocker locker (&contexts.lock ()); cc->second.propagate (lost); } @@ -180,7 +193,11 @@ LocalProcessorCellContexts::compute_results (LocalProcessorContexts &contexts, d std::set gained; std::set_difference (res.begin (), res.end (), common.begin (), common.end (), std::inserter (gained, gained.end ())); - c->second.propagate (gained); + + { + tl::MutexLocker locker (&contexts.lock ()); + c->second.propagate (gained); + } } @@ -275,7 +292,7 @@ public: if (mp_layout) { // In order to guarantee the refs come from the subject layout, we'd need to - // rewrite them to the subject layout if required. + // rewrite them if (!mp_result->has_shape_id (id2)) { db::shape_reference_translator rt (mp_layout); mp_result->add_shape (id2, rt (*ref2)); @@ -542,21 +559,70 @@ private: } +// --------------------------------------------------------------------------------------------- +// LocalProcessorContextComputationTask implementation + +LocalProcessorContextComputationTask::LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::set > &intruders, db::Coord dist) + : tl::Task (), + mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context), + mp_subject_parent (subject_parent), mp_subject_cell (subject_cell), m_subject_cell_inst (subject_cell_inst), + mp_intruder_cell (intruder_cell), m_dist (dist) +{ + // This is quick, but will take away the intruders from the caller + m_intruders.swap (intruders); +} + +void +LocalProcessorContextComputationTask::perform () +{ + mp_proc->compute_contexts (*mp_contexts, mp_parent_context, mp_subject_parent, mp_subject_cell, m_subject_cell_inst, mp_intruder_cell, m_intruders, m_dist); +} + +// --------------------------------------------------------------------------------------------- +// LocalProcessorResultComputationTask implementation + +LocalProcessorResultComputationTask::LocalProcessorResultComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::Cell *cell, LocalProcessorCellContexts *cell_contexts, const LocalOperation *op, unsigned int output_layer) + : mp_proc (proc), mp_contexts (&contexts), mp_cell (cell), mp_cell_contexts (cell_contexts), mp_op (op), m_output_layer (output_layer) +{ + // .. nothing yet .. +} + +void +LocalProcessorResultComputationTask::perform () +{ + mp_cell_contexts->compute_results (*mp_contexts, mp_cell, mp_op, m_output_layer, mp_proc); + + // erase the contexts we don't need any longer + { + tl::MutexLocker locker (& mp_contexts->lock ()); + mp_contexts->context_map ().erase (mp_cell); + } +} + // --------------------------------------------------------------------------------------------- // LocalProcessor implementation LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top) - : mp_subject_layout (layout), mp_intruder_layout (layout), mp_subject_top (top), mp_intruder_top (top) + : mp_subject_layout (layout), mp_intruder_layout (layout), mp_subject_top (top), mp_intruder_top (top), m_nthreads (0) { // .. nothing yet .. } LocalProcessor::LocalProcessor (db::Layout *subject_layout, db::Cell *subject_top, const db::Layout *intruder_layout, const db::Cell *intruder_top) - : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), mp_subject_top (subject_top), mp_intruder_top (intruder_top) + : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), mp_subject_top (subject_top), mp_intruder_top (intruder_top), m_nthreads (0) { // .. nothing yet .. } +std::string LocalProcessor::description (const LocalOperation *op) const +{ + if (op && m_description.empty ()) { + return op->description (); + } else { + return m_description; + } +} + void LocalProcessor::run (LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer) { LocalProcessorContexts contexts; @@ -567,21 +633,57 @@ void LocalProcessor::run (LocalOperation *op, unsigned int subject_layer, unsign void LocalProcessor::push_results (db::Cell *cell, unsigned int output_layer, const std::set &result) const { if (! result.empty ()) { + tl::MutexLocker locker (&cell->layout ()->lock ()); cell->shapes (output_layer).insert (result.begin (), result.end ()); } } -void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer) +void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer) const { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing contexts for ")) + description ()); + try { - contexts.clear (); - contexts.set_intruder_layer (intruder_layer); - contexts.set_subject_layer (subject_layer); - contexts.set_description (op->description ()); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing contexts for ")) + description (op)); - std::pair, std::set > intruders; - compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ()); + if (m_nthreads > 0) { + mp_cc_job.reset (new tl::Job (m_nthreads)); + } else { + mp_cc_job.reset (0); + } + + contexts.clear (); + contexts.set_intruder_layer (intruder_layer); + contexts.set_subject_layer (subject_layer); + + std::pair, std::set > intruders; + issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ()); + + if (mp_cc_job.get ()) { + mp_cc_job->start (); + mp_cc_job->wait (); + } + + } catch (...) { + mp_cc_job.reset (0); + throw; + } +} + +void LocalProcessor::issue_compute_contexts (LocalProcessorContexts &contexts, + db::LocalProcessorCellContext *parent_context, + db::Cell *subject_parent, + db::Cell *subject_cell, + const db::ICplxTrans &subject_cell_inst, + const db::Cell *intruder_cell, + std::pair, std::set > &intruders, + db::Coord dist) const +{ + bool is_small_job = subject_cell->begin ().at_end (); + + if (! is_small_job && mp_cc_job.get ()) { + mp_cc_job->schedule (new LocalProcessorContextComputationTask (this, contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist)); + } else { + compute_contexts (contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist); + } } void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, @@ -591,7 +693,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::set > &intruders, - db::Coord dist) + db::Coord dist) const { if (tl::verbosity () >= 30) { if (! subject_parent) { @@ -601,16 +703,28 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, } } - db::LocalProcessorCellContexts &cell_contexts = contexts.contexts_per_cell (subject_cell, intruder_cell); + db::LocalProcessorCellContext *cell_context = 0; - db::LocalProcessorCellContext *context = cell_contexts.find_context (intruders); - if (context) { - context->add (parent_context, subject_parent, subject_cell_inst); - return; + // prepare a new cell context: this has to happen in a thread-safe way as we share the contexts + // object between threads + + { + tl::MutexLocker locker (& contexts.lock ()); + + db::LocalProcessorCellContexts &cell_contexts = contexts.contexts_per_cell (subject_cell, intruder_cell); + + cell_context = cell_contexts.find_context (intruders); + if (cell_context) { + // we already have a context for this intruder scheme + cell_context->add (parent_context, subject_parent, subject_cell_inst); + return; + } + + cell_context = cell_contexts.create (intruders); + cell_context->add (parent_context, subject_parent, subject_cell_inst); } - context = cell_contexts.create (intruders); - context->add (parent_context, subject_parent, subject_cell_inst); + // perform the actual task .. const db::Shapes *intruder_shapes = 0; if (intruder_cell) { @@ -744,7 +858,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, } db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? &subject_child_cell : 0); - compute_contexts (contexts, context, subject_cell, &subject_child_cell, tn, intruder_child_cell, intruders_below, dist); + issue_compute_contexts (contexts, cell_context, subject_cell, &subject_child_cell, tn, intruder_child_cell, intruders_below, dist); } @@ -756,27 +870,91 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, } void -LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int output_layer) +LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int output_layer) const { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing results for ")) + description ()); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing results for ")) + description (op)); // avoids updates while we work on the layout mp_subject_layout->update (); - db::LayoutLocker locker (mp_subject_layout); + db::LayoutLocker layout_update_locker (mp_subject_layout); - for (db::Layout::bottom_up_const_iterator bu = mp_subject_layout->begin_bottom_up (); bu != mp_subject_layout->end_bottom_up (); ++bu) { + if (m_nthreads > 0) { + + std::auto_ptr > rc_job (new tl::Job (m_nthreads)); + + // schedule computation jobs in "waves": we need to make sure they are executed + // bottom-up. So we identify a new bunch of cells each time we pass through the cell set + // and proceed until all cells are removed. + + std::vector cells_bu; + cells_bu.reserve (mp_subject_layout->cells ()); + for (db::Layout::bottom_up_const_iterator bu = mp_subject_layout->begin_bottom_up (); bu != mp_subject_layout->end_bottom_up (); ++bu) { + cells_bu.push_back (*bu); + } + + int iter = 0; + while (true) { + + ++iter; + tl::SelfTimer timer (tl::verbosity () >= 21, tl::sprintf (tl::to_string (tr ("Computing results iteration #%d")), iter)); + + bool any = false; + std::set later; + + std::vector next_cells_bu; + next_cells_bu.reserve (cells_bu.size ()); + + std::list tasks; + + for (std::vector::const_iterator bu = cells_bu.begin (); bu != cells_bu.end (); ++bu) { + + if (later.find (*bu) == later.end ()) { + + LocalProcessorContexts::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu)); + if (cpc != contexts.context_map ().end ()) { + rc_job->schedule (new LocalProcessorResultComputationTask (this, contexts, cpc->first, &cpc->second, op, output_layer)); + any = true; + for (db::Cell::parent_cell_iterator pc = cpc->first->begin_parent_cells (); pc != cpc->first->end_parent_cells (); ++pc) { + later.insert (*pc); + } + } + + } else { + next_cells_bu.push_back (*bu); + } + + } + + cells_bu.swap (next_cells_bu); + + if (! any) { + break; + } + + if (rc_job.get ()) { + rc_job->start (); + rc_job->wait (); + } + + } + + } else { + + for (db::Layout::bottom_up_const_iterator bu = mp_subject_layout->begin_bottom_up (); bu != mp_subject_layout->end_bottom_up (); ++bu) { + + LocalProcessorContexts::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu)); + if (cpc != contexts.context_map ().end ()) { + cpc->second.compute_results (contexts, cpc->first, op, output_layer, this); + contexts.context_map ().erase (cpc); + } - LocalProcessorContexts::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu)); - if (cpc != contexts.context_map ().end ()) { - cpc->second.compute_results (contexts, cpc->first, op, output_layer, this); - contexts.context_map ().erase (cpc); } } } void -LocalProcessor::compute_local_cell (LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const db::LocalOperation *op, const std::pair, std::set > &intruders, std::set &result) +LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const db::LocalOperation *op, const std::pair, std::set > &intruders, std::set &result) const { const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ()); diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index f99235418..948352e13 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -28,6 +28,7 @@ #include "dbLayout.h" #include "dbPluginCommon.h" #include "dbLocalOperation.h" +#include "tlThreadedWorkers.h" #include #include @@ -40,7 +41,7 @@ class LocalProcessor; class LocalProcessorCellContext; class LocalProcessorContexts; -// @@@ TODO: move this somewhere else? +// TODO: move this somewhere else? class DB_PLUGIN_PUBLIC ShapeInteractions { public: @@ -78,7 +79,7 @@ private: unsigned int m_id; }; -// @@@ TODO: should be hidden (private data?) +// TODO: should be hidden (private data?) struct DB_PLUGIN_PUBLIC LocalProcessorCellDrop { LocalProcessorCellDrop (db::LocalProcessorCellContext *_parent_context, db::Cell *_parent, const db::ICplxTrans &_cell_inst) @@ -92,7 +93,7 @@ struct DB_PLUGIN_PUBLIC LocalProcessorCellDrop db::ICplxTrans cell_inst; }; -// @@@ TODO: should be hidden (private data?) +// TODO: should be hidden (private data?) class DB_PLUGIN_PUBLIC LocalProcessorCellContext { public: @@ -108,6 +109,11 @@ public: return m_propagated; } + const std::set &propagated () const + { + return m_propagated; + } + size_t size () const { return m_drops.size (); @@ -130,7 +136,7 @@ public: db::LocalProcessorCellContext *find_context (const key_type &intruders); db::LocalProcessorCellContext *create (const key_type &intruders); - void compute_results (LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, LocalProcessor *proc); + void compute_results (const LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, const LocalProcessor *proc); iterator begin () const { @@ -208,20 +214,82 @@ public: return m_intruder_layer; } - void set_description (const std::string &desc) + tl::Mutex &lock () const { - m_description = desc; - } - - const std::string &description () const - { - return m_description; + return m_lock; } private: contexts_per_cell_type m_contexts_per_cell; unsigned int m_subject_layer, m_intruder_layer; - std::string m_description; + mutable tl::Mutex m_lock; +}; + +class DB_PLUGIN_PUBLIC LocalProcessorContextComputationTask + : public tl::Task +{ +public: + LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::set > &intruders, db::Coord dist); + void perform (); + +private: + const LocalProcessor *mp_proc; + LocalProcessorContexts *mp_contexts; + db::LocalProcessorCellContext *mp_parent_context; + db::Cell *mp_subject_parent; + db::Cell *mp_subject_cell; + db::ICplxTrans m_subject_cell_inst; + const db::Cell *mp_intruder_cell; + std::pair, std::set > m_intruders; + db::Coord m_dist; +}; + +class DB_PLUGIN_PUBLIC LocalProcessorContextComputationWorker + : public tl::Worker +{ +public: + LocalProcessorContextComputationWorker () + : tl::Worker () + { + // .. nothing yet .. + } + + void perform_task (tl::Task *task) + { + static_cast (task)->perform (); + } +}; + +class DB_PLUGIN_PUBLIC LocalProcessorResultComputationTask + : public tl::Task +{ +public: + LocalProcessorResultComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::Cell *cell, LocalProcessorCellContexts *cell_contexts, const LocalOperation *op, unsigned int output_layer); + void perform (); + +private: + const LocalProcessor *mp_proc; + LocalProcessorContexts *mp_contexts; + db::Cell *mp_cell; + LocalProcessorCellContexts *mp_cell_contexts; + const LocalOperation *mp_op; + unsigned int m_output_layer; +}; + +class DB_PLUGIN_PUBLIC LocalProcessorResultComputationWorker + : public tl::Worker +{ +public: + LocalProcessorResultComputationWorker () + : tl::Worker () + { + // .. nothing yet .. + } + + void perform_task (tl::Task *task) + { + static_cast (task)->perform (); + } }; class DB_PLUGIN_PUBLIC LocalProcessor @@ -230,31 +298,37 @@ public: LocalProcessor (db::Layout *layout, db::Cell *top); LocalProcessor (db::Layout *subject_layout, db::Cell *subject_top, const db::Layout *intruder_layout, const db::Cell *intruder_cell); void run (LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer); - void compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer); - void compute_results (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int output_layer); + void compute_contexts (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer) const; + void compute_results (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int output_layer) const; void set_description (const std::string &d) { m_description = d; } - const std::string &description () const + void set_threads (unsigned int nthreads) { - return m_description; + m_nthreads = nthreads; } private: friend class LocalProcessorCellContexts; + friend class LocalProcessorContextComputationTask; db::Layout *mp_subject_layout; const db::Layout *mp_intruder_layout; db::Cell *mp_subject_top; const db::Cell *mp_intruder_top; std::string m_description; + unsigned int m_nthreads; + mutable std::auto_ptr > mp_cc_job; - void compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::set > &intruders, db::Coord dist); + std::string description (const LocalOperation *op) const; + void compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::set > &intruders, db::Coord dist) const; + void do_compute_contexts (db::LocalProcessorCellContext *cell_context, const db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::set > &intruders, db::Coord dist) const; + void issue_compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::set > &intruders, db::Coord dist) const; void push_results (db::Cell *cell, unsigned int output_layer, const std::set &result) const; - void compute_local_cell (LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const std::pair, std::set > &intruders, std::set &result); + void compute_local_cell (const db::LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const std::pair, std::set > &intruders, std::set &result) const; }; } diff --git a/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc b/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc index 2ee0846b8..744342b0b 100644 --- a/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc +++ b/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc @@ -56,6 +56,7 @@ public: */ virtual void put (const db::Polygon &polygon) { + tl::MutexLocker locker (&mp_layout->lock ()); mp_polyrefs->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); } diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc index 3818caae6..a8d7edfcb 100644 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc @@ -35,7 +35,7 @@ namespace db { NetExtractor::NetExtractor() - : mp_orig_layout (0), mp_layout (0), mp_top_cell (0) + : mp_orig_layout (0), mp_layout (0), mp_top_cell (0), m_nthreads (0) { // @@@ @@ -49,6 +49,12 @@ NetExtractor::~NetExtractor () mp_top_cell = 0; } +void +NetExtractor::set_threads (unsigned int nthreads) +{ + m_nthreads = nthreads; +} + void NetExtractor::open (const db::Layout &orig_layout, cell_index_type orig_top_cell) { @@ -154,6 +160,7 @@ NetExtractor::and_or_not (NetLayer a, NetLayer b, bool is_and) db::BoolAndOrNotLocalOperation op (is_and); db::LocalProcessor proc (mp_layout, mp_top_cell); + proc.set_threads (m_nthreads); proc.run (&op, a.layer_index (), b.layer_index (), lout); return NetLayer (lout); diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.h b/src/plugins/tools/netx/db_plugin/dbNetExtractor.h index d4066a3ae..979ec3a97 100644 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.h +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.h @@ -74,6 +74,12 @@ public: void output (NetLayer a, const db::LayerProperties &lp); db::Layout *layout_copy () const; + void set_threads (unsigned int nthreads); + unsigned int threads () const + { + return m_nthreads; + } + private: // no copying NetExtractor (const db::NetExtractor &); @@ -86,6 +92,7 @@ private: db::Layout *mp_layout; db::Cell *mp_top_cell; db::CellMapping m_cm; + unsigned int m_nthreads; }; } diff --git a/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc index d6ee3c1df..225b5bf1f 100644 --- a/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc +++ b/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc @@ -55,6 +55,12 @@ gsi::Class decl_NetNetExtractor ("db", "NetExtractor", gsi::method ("open", &db::NetExtractor::open, gsi::arg ("orig_layout"), gsi::arg ("orig_top_cell_index"), "@@@" ) + + gsi::method ("threads=", &db::NetExtractor::set_threads, gsi::arg ("nthreads"), + "@@@" + ) + + gsi::method ("threads", &db::NetExtractor::threads, + "@@@" + ) + gsi::method_ext ("open", &open2, gsi::arg ("orig_layout"), gsi::arg ("orig_top_cell"), "@@@" ) + diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index db783ecff..d28ee65b5 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -158,7 +158,7 @@ std::string contexts_to_s (db::Layout *layout, db::LocalProcessorContexts &conte return res; } -void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc, bool single, db::Coord dist) +void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc, bool single, db::Coord dist, unsigned int nthreads = 0) { db::Layout layout_org; @@ -225,6 +225,7 @@ void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, in if (single) { db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); + proc.set_threads (nthreads); if (! context_doc) { proc.run (lop, l1, l2, lout); @@ -240,6 +241,7 @@ void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, in db::Layout layout_org2 = layout_org; db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &layout_org2, &layout_org2.cell (*layout_org2.begin_top_down ())); + proc.set_threads (nthreads); if (! context_doc) { proc.run (lop, l1, l2, lout); @@ -255,24 +257,24 @@ void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, in db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); } -void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0) +void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) { - run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true, 0); + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true, 0, nthreads); } -void run_test_bool2 (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0) +void run_test_bool2 (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) { - run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false, 0); + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false, 0, nthreads); } -void run_test_bool_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0) +void run_test_bool_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) { - run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true, dist); + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true, dist, nthreads); } -void run_test_bool2_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0) +void run_test_bool2_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) { - run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false, dist); + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false, dist, nthreads); } TEST(BasicAnd1) @@ -281,36 +283,108 @@ TEST(BasicAnd1) run_test_bool (_this, "hlp1.oas", TMAnd, 100); } +TEST(BasicAnd1SingleThread) +{ + // Simple flat AND + run_test_bool (_this, "hlp1.oas", TMAnd, 100, 0, 1); +} + +TEST(BasicAnd1FourThreads) +{ + // Simple flat AND + run_test_bool (_this, "hlp1.oas", TMAnd, 100, 0, 4); +} + TEST(BasicNot1) { - // Simple flat NOT + // Simple flat AND run_test_bool (_this, "hlp1.oas", TMNot, 101); } +TEST(BasicNot1SingleThread) +{ + // Simple flat NOT + run_test_bool (_this, "hlp1.oas", TMNot, 101, 0, 1); +} + +TEST(BasicNot1FourThreads) +{ + // Simple flat NOT + run_test_bool (_this, "hlp1.oas", TMNot, 101, 0, 4); +} + TEST(BasicAnd2) { // Up/down and down/up interactions, AND run_test_bool (_this, "hlp2.oas", TMAnd, 100); } +TEST(BasicAnd2SingleThread) +{ + // Up/down and down/up interactions, AND + run_test_bool (_this, "hlp2.oas", TMAnd, 100, 0, 1); +} + +TEST(BasicAnd2FourThreads) +{ + // Up/down and down/up interactions, AND + run_test_bool (_this, "hlp2.oas", TMAnd, 100, 0, 4); +} + TEST(BasicNot2) { // Up/down and down/up interactions, NOT run_test_bool (_this, "hlp2.oas", TMNot, 101); } +TEST(BasicNot2SingleThread) +{ + // Up/down and down/up interactions, NOT + run_test_bool (_this, "hlp2.oas", TMNot, 101, 0, 1); +} + +TEST(BasicNot2FourThreads) +{ + // Up/down and down/up interactions, NOT + run_test_bool (_this, "hlp2.oas", TMNot, 101, 0, 4); +} + TEST(BasicAnd3) { // Variant building, AND run_test_bool (_this, "hlp3.oas", TMAnd, 100); } +TEST(BasicAnd3SingleThread) +{ + // Variant building, AND + run_test_bool (_this, "hlp3.oas", TMAnd, 100, 0, 1); +} + +TEST(BasicAnd3FourThreads) +{ + // Variant building, AND + run_test_bool (_this, "hlp3.oas", TMAnd, 100, 0, 4); +} + TEST(BasicNot3) { // Variant building, NOT run_test_bool (_this, "hlp3.oas", TMNot, 101); } +TEST(BasicNot3SingleThread) +{ + // Variant building, NOT + run_test_bool (_this, "hlp3.oas", TMNot, 101, 0, 1); +} + +TEST(BasicNot3FourThreads) +{ + // Variant building, NOT + run_test_bool (_this, "hlp3.oas", TMNot, 101, 0, 4); +} + TEST(BasicAnd4) { // Sibling interactions, variant building, AND From 17f53cf54ec191ef836ecef7da7448d6b94d2d64 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 3 Nov 2018 12:28:27 +0100 Subject: [PATCH 032/335] WIP: some performance improvements, cronology debugging support (experimental) --- src/klayout.pri | 6 + .../tools/netx/db_plugin/dbHierProcessor.cc | 257 +++++++++++++----- .../tools/netx/db_plugin/dbHierProcessor.h | 82 ++++-- .../tools/netx/db_plugin/dbLocalOperation.cc | 8 +- .../tools/netx/db_plugin/dbLocalOperation.h | 10 +- .../netx/unit_tests/dbHierProcessorTests.cc | 4 +- 6 files changed, 277 insertions(+), 90 deletions(-) diff --git a/src/klayout.pri b/src/klayout.pri index 298273ebc..c09f225ce 100644 --- a/src/klayout.pri +++ b/src/klayout.pri @@ -97,6 +97,12 @@ equals(HAVE_RUBY, "1") { } } +equals(HAVE_CRONOLOGY, "1") { + DEFINES += HAVE_CRONOLOGY + LIBS += $$CRONOLOGY_LIB + INCLUDEPATH += $$CRONOLOGY_INCLUDE +} + msvc { QMAKE_CXXFLAGS += \ diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc index faf06d33e..d390a4ab4 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -31,6 +31,33 @@ #include "tlTimer.h" #include "tlInternational.h" +// --------------------------------------------------------------------------------------------- +// Cronology debugging support (TODO: experimental) + +#if defined(HAVE_CRONOLOGY) + +#include + +CRONOLOGY_MAKE_EVENT(event_compute_contexts, "Compute contexts") +CRONOLOGY_MAKE_EVENT(event_compute_contexts_unlocked, "Compute contexts (unlocked)") + +cronology::events::event_collection collect_events; + +#define CRONOLOGY_COLLECTION_BRACKET(event_name) cronology::EventBracket __bracket##event_name (collect_events.threadwise ()->event ().event ()); + +CRONOLOGY_MAKE_EVENT(event_compute_results, "Compute results") +CRONOLOGY_MAKE_EVENT(event_compute_local_cell, "Compute local cell results") +CRONOLOGY_MAKE_EVENT(event_propagate, "Propagate local cell results") + +cronology::events::event_collection compute_events; + +#define CRONOLOGY_COMPUTE_BRACKET(event_name) cronology::EventBracket __bracket##event_name (compute_events.threadwise ()->event ().event ()); + +#else +#define CRONOLOGY_COLLECTION_BRACKET(event_name) +#define CRONOLOGY_COMPUTE_BRACKET(event_name) +#endif + namespace db { @@ -42,6 +69,7 @@ class shape_reference_translator { public: typedef typename Ref::shape_type shape_type; + typedef typename Ref::trans_type ref_trans_type; shape_reference_translator (db::Layout *target_layout) : mp_layout (target_layout) @@ -51,21 +79,102 @@ public: Ref operator() (const Ref &ref) const { - tl::MutexLocker locker (&mp_layout->lock ()); - shape_type sh = ref.obj ().transformed (ref.trans ()); - return Ref (sh, mp_layout->shape_repository ()); + typename std::unordered_map::const_iterator m = m_cache.find (ref.ptr ()); + if (m != m_cache.end ()) { + + return Ref (m->second, ref.trans ()); + + } else { + + const shape_type *ptr; + { + tl::MutexLocker locker (&mp_layout->lock ()); + ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (ref.obj ()); + } + + m_cache[ref.ptr ()] = ptr; + return Ref (ptr, ref.trans ()); + + } } template Ref operator() (const Ref &ref, const Trans &tr) const { - tl::MutexLocker locker (&mp_layout->lock ()); - shape_type sh = ref.obj ().transformed (tr * Trans (ref.trans ())); - return Ref (sh, mp_layout->shape_repository ()); + shape_type sh = ref.obj ().transformed (Trans (ref_trans_type (tr).inverted ()) * tr); + ref_trans_type red_trans; + sh.reduce (red_trans); + + typename std::unordered_map >::const_iterator m = m_cache_by_shape.find (sh); + if (m != m_cache_by_shape.end ()) { + + return Ref (m->second.first, ref_trans_type (tr * Trans (ref.trans ())) * m->second.second); + + } else { + + const shape_type *ptr; + { + tl::MutexLocker locker (&mp_layout->lock ()); + ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (sh); + } + + m_cache_by_shape[sh] = std::make_pair (ptr, red_trans); + return Ref (ptr, ref_trans_type (tr * Trans (ref.trans ())) * red_trans); + + } } private: db::Layout *mp_layout; + mutable std::unordered_map m_cache; + mutable std::unordered_map > m_cache_by_shape; +}; + +template +class shape_reference_translator_with_trans +{ +public: + typedef typename Ref::shape_type shape_type; + typedef typename Ref::trans_type ref_trans_type; + + shape_reference_translator_with_trans (db::Layout *target_layout, const Trans &trans) + : mp_layout (target_layout), m_trans (trans), m_ref_trans (trans), m_bare_trans (Trans (m_ref_trans.inverted ()) * trans) + { + // .. nothing yet .. + } + + Ref operator() (const Ref &ref) const + { + typename std::unordered_map >::const_iterator m = m_cache.find (ref.ptr ()); + if (m != m_cache.end ()) { + + return Ref (m->second.first, ref_trans_type (m_trans * Trans (ref.trans ())) * m->second.second); + + } else { + + shape_type sh = ref.obj ().transformed (m_bare_trans); + ref_trans_type red_trans; + sh.reduce (red_trans); + + const shape_type *ptr; + { + tl::MutexLocker locker (&mp_layout->lock ()); + ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (sh); + } + + m_cache[ref.ptr ()] = std::make_pair (ptr, red_trans); + + return Ref (ptr, ref_trans_type (m_trans * Trans (ref.trans ())) * red_trans); + + } + } + +private: + db::Layout *mp_layout; + Trans m_trans; + ref_trans_type m_ref_trans; + Trans m_bare_trans; + mutable std::unordered_map > m_cache; }; // --------------------------------------------------------------------------------------------- @@ -83,7 +192,7 @@ LocalProcessorCellContext::add (db::LocalProcessorCellContext *parent_context, d } void -LocalProcessorCellContext::propagate (const std::set &res) +LocalProcessorCellContext::propagate (const std::unordered_set &res) { if (res.empty ()) { return; @@ -95,13 +204,19 @@ LocalProcessorCellContext::propagate (const std::set &res) tl_assert (d->parent != 0); db::Layout *subject_layout = d->parent->layout (); - shape_reference_translator rt (subject_layout); - for (std::set::const_iterator r = res.begin (); r != res.end (); ++r) { - d->parent_context->propagated ().insert (rt (*r, d->cell_inst)); + shape_reference_translator_with_trans rt (subject_layout, d->cell_inst); + std::vector new_refs; + new_refs.reserve (res.size ()); + for (std::unordered_set::const_iterator r = res.begin (); r != res.end (); ++r) { + new_refs.push_back (rt (*r)); + } + + { + tl::MutexLocker locker (&d->parent_context->lock ()); + d->parent_context->propagated ().insert (new_refs.begin (), new_refs.end ()); } } - } // --------------------------------------------------------------------------------------------- @@ -122,7 +237,7 @@ LocalProcessorCellContexts::LocalProcessorCellContexts (const db::Cell *intruder db::LocalProcessorCellContext * LocalProcessorCellContexts::find_context (const key_type &intruders) { - std::map::iterator c = m_contexts.find (intruders); + std::unordered_map::iterator c = m_contexts.find (intruders); return c != m_contexts.end () ? &c->second : 0; } @@ -135,12 +250,14 @@ LocalProcessorCellContexts::create (const key_type &intruders) void LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, const LocalProcessor *proc) { + CRONOLOGY_COMPUTE_BRACKET(event_compute_results) + bool first = true; - std::set common; + std::unordered_set common; int index = 0; int total = int (m_contexts.size ()); - for (std::map::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { + for (std::unordered_map::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { ++index; @@ -151,54 +268,68 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte if (first) { { - tl::MutexLocker locker (&contexts.lock ()); + tl::MutexLocker locker (&c->second.lock ()); common = c->second.propagated (); } + CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell) proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, common); first = false; } else { - std::set res; + std::unordered_set res; { - tl::MutexLocker locker (&contexts.lock ()); + tl::MutexLocker locker (&c->second.lock ()); res = c->second.propagated (); } - proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, res); + { + CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell) + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, res); + } if (common.empty ()) { - tl::MutexLocker locker (&contexts.lock ()); + CRONOLOGY_COMPUTE_BRACKET(event_propagate) c->second.propagate (res); } else if (res != common) { - std::set lost; - std::set_difference (common.begin (), common.end (), res.begin (), res.end (), std::inserter (lost, lost.end ())); + CRONOLOGY_COMPUTE_BRACKET(event_propagate) + + std::unordered_set lost; + for (std::unordered_set::const_iterator i = common.begin (); i != common.end (); ++i) { + if (res.find (*i) == res.end ()) { + lost.insert (*i); + } + } if (! lost.empty ()) { - std::set new_common; - std::set_intersection (common.begin (), common.end (), res.begin (), res.end (), std::inserter (new_common, new_common.end ())); + std::unordered_set new_common; + for (std::unordered_set::const_iterator i = common.begin (); i != common.end (); ++i) { + if (res.find (*i) != res.end ()) { + new_common.insert (*i); + } + } common.swap (new_common); - for (std::map::iterator cc = m_contexts.begin (); cc != c; ++cc) { - tl::MutexLocker locker (&contexts.lock ()); + for (std::unordered_map::iterator cc = m_contexts.begin (); cc != c; ++cc) { cc->second.propagate (lost); } } - std::set gained; - std::set_difference (res.begin (), res.end (), common.begin (), common.end (), std::inserter (gained, gained.end ())); - - { - tl::MutexLocker locker (&contexts.lock ()); - c->second.propagate (gained); + std::unordered_set gained; + for (std::unordered_set::const_iterator i = res.begin (); i != res.end (); ++i) { + if (common.find (*i) == common.end ()) { + gained.insert (*i); + } } + c->second.propagate (gained); + } } @@ -256,7 +387,7 @@ ShapeInteractions::intruders_for (unsigned int subject_id) const const db::PolygonRef & ShapeInteractions::shape (unsigned int id) const { - std::map::const_iterator i = m_shapes.find (id); + std::unordered_map::const_iterator i = m_shapes.find (id); if (i == m_shapes.end ()) { static db::PolygonRef s; return s; @@ -363,7 +494,7 @@ private: unsigned int m_intruder_layer; db::Coord m_dist; ShapeInteractions *mp_result; - std::map, unsigned int> m_inst_shape_ids; + std::unordered_map, unsigned int> m_inst_shape_ids; void add_shapes_from_intruder_inst (unsigned int id1, const db::Cell &intruder_cell, const db::ICplxTrans &tn, unsigned int inst_id, const db::Box ®ion) { @@ -380,7 +511,7 @@ private: // reuse the same id for shapes from the same instance -> this avoid duplicates with different IDs on // the intruder side. - std::map, unsigned int>::const_iterator k = m_inst_shape_ids.find (std::make_pair (inst_id, ref2)); + std::unordered_map, unsigned int>::const_iterator k = m_inst_shape_ids.find (std::make_pair (inst_id, ref2)); if (k == m_inst_shape_ids.end ()) { k = m_inst_shape_ids.insert (std::make_pair (std::make_pair (inst_id, ref2), mp_result->next_id ())).first; @@ -408,7 +539,7 @@ instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, u const db::Cell &cell2 = layout2->cell (inst2->object ().cell_index ()); db::box_convert inst2_bc (*layout2, layer2); - std::set relative_trans_seen; + std::unordered_set relative_trans_seen; for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) { @@ -465,9 +596,9 @@ struct InteractionRegistrationInst2Inst : db::box_scanner_receiver2 { public: - typedef std::pair, std::set > interaction_value_type; + typedef std::pair, std::unordered_set > interaction_value_type; - InteractionRegistrationInst2Inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, db::Coord dist, std::map *result) + InteractionRegistrationInst2Inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, db::Coord dist, std::unordered_map *result) : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result) { // nothing yet .. @@ -500,8 +631,8 @@ private: const db::Layout *mp_subject_layout, *mp_intruder_layout; unsigned int m_subject_layer, m_intruder_layer; db::Coord m_dist; - std::map, std::set > > *mp_result; - std::set > m_interactions; + std::unordered_map, std::unordered_set > > *mp_result; + std::unordered_set > m_interactions; }; static bool @@ -537,7 +668,7 @@ struct InteractionRegistrationInst2Shape : db::box_scanner_receiver2 { public: - InteractionRegistrationInst2Shape (const db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, std::map, std::set > > *result) + InteractionRegistrationInst2Shape (const db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, std::unordered_map, std::unordered_set > > *result) : mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result) { // nothing yet .. @@ -554,7 +685,7 @@ private: const db::Layout *mp_subject_layout; unsigned int m_subject_layer; db::Coord m_dist; - std::map, std::set > > *mp_result; + std::unordered_map, std::unordered_set > > *mp_result; }; } @@ -562,7 +693,7 @@ private: // --------------------------------------------------------------------------------------------- // LocalProcessorContextComputationTask implementation -LocalProcessorContextComputationTask::LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::set > &intruders, db::Coord dist) +LocalProcessorContextComputationTask::LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::unordered_set > &intruders, db::Coord dist) : tl::Task (), mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context), mp_subject_parent (subject_parent), mp_subject_cell (subject_cell), m_subject_cell_inst (subject_cell_inst), @@ -630,7 +761,7 @@ void LocalProcessor::run (LocalOperation *op, unsigned int subject_layer, unsign compute_results (contexts, op, output_layer); } -void LocalProcessor::push_results (db::Cell *cell, unsigned int output_layer, const std::set &result) const +void LocalProcessor::push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set &result) const { if (! result.empty ()) { tl::MutexLocker locker (&cell->layout ()->lock ()); @@ -654,7 +785,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const L contexts.set_intruder_layer (intruder_layer); contexts.set_subject_layer (subject_layer); - std::pair, std::set > intruders; + std::pair, std::unordered_set > intruders; issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ()); if (mp_cc_job.get ()) { @@ -674,7 +805,7 @@ void LocalProcessor::issue_compute_contexts (LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, - std::pair, std::set > &intruders, + std::pair, std::unordered_set > &intruders, db::Coord dist) const { bool is_small_job = subject_cell->begin ().at_end (); @@ -692,9 +823,11 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, - const std::pair, std::set > &intruders, + const std::pair, std::unordered_set > &intruders, db::Coord dist) const { + CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts) + if (tl::verbosity () >= 30) { if (! subject_parent) { tl::log << tr ("Computing context for top cell ") << mp_subject_layout->cell_name (subject_cell->cell_index ()); @@ -726,6 +859,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, // perform the actual task .. + CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts_unlocked) const db::Shapes *intruder_shapes = 0; if (intruder_cell) { intruder_shapes = &intruder_cell->shapes (contexts.intruder_layer ()); @@ -740,9 +874,9 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, if (! subject_cell->begin ().at_end ()) { - typedef std::pair, std::set > interaction_value_type; + typedef std::pair, std::unordered_set > interaction_value_type; - std::map interactions; + std::unordered_map interactions; // insert dummy interactions to handle at least the child cell vs. itself // - this is important so we will always handle the instances unless they are @@ -792,7 +926,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, } - for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + for (std::unordered_set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { if (! inst_bci (*i).empty ()) { scanner.insert2 (i.operator-> (), ++id); } @@ -811,7 +945,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, } } - for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + for (std::unordered_set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert2 (i.operator-> (), 0); } @@ -824,10 +958,9 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, scanner.process (rec, dist, inst_bcs, db::box_convert ()); } - for (std::map::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (std::unordered_map::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { db::Cell &subject_child_cell = mp_subject_layout->cell (i->first->object ().cell_index ()); - db::shape_reference_translator rt (mp_subject_layout); for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) { @@ -837,17 +970,19 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, if (! nbox.empty ()) { - std::pair, std::set > intruders_below; + std::pair, std::unordered_set > intruders_below; - for (std::set::const_iterator p = i->second.second.begin (); p != i->second.second.end (); ++p) { + db::shape_reference_translator_with_trans rt (mp_subject_layout, tni); + + for (std::unordered_set::const_iterator p = i->second.second.begin (); p != i->second.second.end (); ++p) { if (nbox.overlaps (p->box ())) { - intruders_below.second.insert (rt (*p, tni)); + intruders_below.second.insert (rt (*p)); } } // TODO: in some cases, it may be possible to optimize this for arrays - for (std::set::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) { + for (std::unordered_set::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) { for (db::CellInstArray::iterator k = (*j)->begin_touching (nbox.enlarged (db::Vector (-1, -1)), inst_bcii); ! k.at_end (); ++k) { db::ICplxTrans tk = (*j)->complex_trans (*k); // NOTE: no self-interactions @@ -899,7 +1034,7 @@ LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOp tl::SelfTimer timer (tl::verbosity () >= 21, tl::sprintf (tl::to_string (tr ("Computing results iteration #%d")), iter)); bool any = false; - std::set later; + std::unordered_set later; std::vector next_cells_bu; next_cells_bu.reserve (cells_bu.size ()); @@ -954,7 +1089,7 @@ LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOp } void -LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const db::LocalOperation *op, const std::pair, std::set > &intruders, std::set &result) const +LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const db::LocalOperation *op, const std::pair, std::unordered_set > &intruders, std::unordered_set &result) const { const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ()); @@ -1001,7 +1136,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: scanner.insert (ref, id++); } - for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + for (std::unordered_set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert (i.operator-> (), interactions.next_id ()); } @@ -1018,7 +1153,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: scanner.insert1 (ref, id++); } - for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + for (std::unordered_set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert2 (i.operator-> (), interactions.next_id ()); } @@ -1060,7 +1195,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: } } - for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + for (std::unordered_set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { if (! inst_bci (*i).empty ()) { scanner.insert2 (i.operator-> (), ++inst_id); } diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h index 948352e13..307d32569 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -33,6 +33,46 @@ #include #include #include +#include +#include + +// @@@ should go into dbHash.h +#include "dbHash.h" + +namespace std +{ + template + struct hash > + { + size_t operator() (const db::disp_trans &t) const + { + return hfunc (t.disp ()); + } + }; + + template + struct hash > + { + size_t operator() (const db::polygon_ref &o) const + { + return hfunc (size_t (o.ptr ()), std::hash () (o.trans ())); + } + }; + + template + struct hash > + { + size_t operator() (const std::unordered_set &o) const + { + size_t hf = 0; + for (typename std::unordered_set::const_iterator i = o.begin (); i != o.end (); ++i) { + hf = hfunc (hf, std::hash () (*i)); + } + return hf; + } + }; +} + namespace db { @@ -45,7 +85,7 @@ class LocalProcessorContexts; class DB_PLUGIN_PUBLIC ShapeInteractions { public: - typedef std::map > container; + typedef std::unordered_map > container; typedef container::const_iterator iterator; typedef container::value_type::second_type::const_iterator iterator2; @@ -74,8 +114,8 @@ public: } private: - std::map > m_interactions; - std::map m_shapes; + std::unordered_map > m_interactions; + std::unordered_map m_shapes; unsigned int m_id; }; @@ -102,14 +142,14 @@ public: LocalProcessorCellContext (); void add (db::LocalProcessorCellContext *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst); - void propagate (const std::set &res); + void propagate (const std::unordered_set &res); - std::set &propagated () + std::unordered_set &propagated () { return m_propagated; } - const std::set &propagated () const + const std::unordered_set &propagated () const { return m_propagated; } @@ -119,16 +159,22 @@ public: return m_drops.size (); } + tl::Mutex &lock () + { + return m_lock; + } + private: - std::set m_propagated; + std::unordered_set m_propagated; std::vector m_drops; + tl::Mutex m_lock; }; class DB_PLUGIN_PUBLIC LocalProcessorCellContexts { public: - typedef std::pair, std::set > key_type; - typedef std::map map_type; + typedef std::pair, std::unordered_set > key_type; + typedef std::unordered_map map_type; typedef map_type::const_iterator iterator; LocalProcessorCellContexts (); @@ -150,13 +196,13 @@ public: private: const db::Cell *mp_intruder_cell; - std::map m_contexts; + std::unordered_map m_contexts; }; class DB_PLUGIN_PUBLIC LocalProcessorContexts { public: - typedef std::map contexts_per_cell_type; + typedef std::unordered_map contexts_per_cell_type; typedef contexts_per_cell_type::iterator iterator; LocalProcessorContexts () @@ -229,7 +275,7 @@ class DB_PLUGIN_PUBLIC LocalProcessorContextComputationTask : public tl::Task { public: - LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::set > &intruders, db::Coord dist); + LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::unordered_set > &intruders, db::Coord dist); void perform (); private: @@ -240,7 +286,7 @@ private: db::Cell *mp_subject_cell; db::ICplxTrans m_subject_cell_inst; const db::Cell *mp_intruder_cell; - std::pair, std::set > m_intruders; + std::pair, std::unordered_set > m_intruders; db::Coord m_dist; }; @@ -324,11 +370,11 @@ private: mutable std::auto_ptr > mp_cc_job; std::string description (const LocalOperation *op) const; - void compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::set > &intruders, db::Coord dist) const; - void do_compute_contexts (db::LocalProcessorCellContext *cell_context, const db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::set > &intruders, db::Coord dist) const; - void issue_compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::set > &intruders, db::Coord dist) const; - void push_results (db::Cell *cell, unsigned int output_layer, const std::set &result) const; - void compute_local_cell (const db::LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const std::pair, std::set > &intruders, std::set &result) const; + void compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::unordered_set > &intruders, db::Coord dist) const; + void do_compute_contexts (db::LocalProcessorCellContext *cell_context, const db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::unordered_set > &intruders, db::Coord dist) const; + void issue_compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::unordered_set > &intruders, db::Coord dist) const; + void push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set &result) const; + void compute_local_cell (const db::LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const std::pair, std::unordered_set > &intruders, std::unordered_set &result) const; }; } diff --git a/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc b/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc index 744342b0b..a9e5300f8 100644 --- a/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc +++ b/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc @@ -47,7 +47,7 @@ public: /** * @brief Constructor specifying an external vector for storing the polygons */ - PolygonRefGenerator (db::Layout *layout, std::set &polyrefs) + PolygonRefGenerator (db::Layout *layout, std::unordered_set &polyrefs) : PolygonSink (), mp_layout (layout), mp_polyrefs (&polyrefs) { } @@ -62,7 +62,7 @@ public: private: db::Layout *mp_layout; - std::set *mp_polyrefs; + std::unordered_set *mp_polyrefs; }; } @@ -88,7 +88,7 @@ BoolAndOrNotLocalOperation::description () const } void -BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const +BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const { db::EdgeProcessor ep; @@ -147,7 +147,7 @@ SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wra // .. nothing yet .. } -void SelfOverlapMergeLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const +void SelfOverlapMergeLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const { if (m_wrap_count == 0) { return; diff --git a/src/plugins/tools/netx/db_plugin/dbLocalOperation.h b/src/plugins/tools/netx/db_plugin/dbLocalOperation.h index 6f726dd24..330d71441 100644 --- a/src/plugins/tools/netx/db_plugin/dbLocalOperation.h +++ b/src/plugins/tools/netx/db_plugin/dbLocalOperation.h @@ -28,8 +28,8 @@ #include "dbLayout.h" #include "dbPluginCommon.h" -#include -#include +#include +#include #include namespace db @@ -87,7 +87,7 @@ public: * @param interactions The interaction set * @param result The container to which the results are written */ - virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const = 0; + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const = 0; /** * @brief Indicates the desired behaviour when a shape does not have an intruder @@ -115,7 +115,7 @@ class DB_PLUGIN_PUBLIC BoolAndOrNotLocalOperation public: BoolAndOrNotLocalOperation (bool is_and); - virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const; + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const; virtual on_empty_intruder_mode on_empty_intruder_hint () const; virtual std::string description () const; @@ -134,7 +134,7 @@ class DB_PLUGIN_PUBLIC SelfOverlapMergeLocalOperation public: SelfOverlapMergeLocalOperation (unsigned int wrap_count); - virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::set &result) const; + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const; virtual on_empty_intruder_mode on_empty_intruder_hint () const; virtual std::string description () const; diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc index d28ee65b5..94ac38232 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -55,7 +55,7 @@ public: // .. nothing yet .. } - virtual void compute_local (db::Layout *layout, const db::ShapeInteractions &interactions, std::set &result) const + virtual void compute_local (db::Layout *layout, const db::ShapeInteractions &interactions, std::unordered_set &result) const { db::ShapeInteractions sized_interactions = interactions; for (db::ShapeInteractions::iterator i = sized_interactions.begin (); i != sized_interactions.end (); ++i) { @@ -91,7 +91,7 @@ public: // .. nothing yet .. } - virtual void compute_local (db::Layout *layout, const db::ShapeInteractions &interactions, std::set &result) const + virtual void compute_local (db::Layout *layout, const db::ShapeInteractions &interactions, std::unordered_set &result) const { db::ShapeInteractions sized_interactions = interactions; for (db::ShapeInteractions::iterator i = sized_interactions.begin (); i != sized_interactions.end (); ++i) { From 54945105efe544c12a6ccd3b467423a1eeeb5779 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 Nov 2018 22:56:08 +0100 Subject: [PATCH 033/335] WIP: first part of region refactoring. --- src/db/db/dbEdges.h | 18 +- src/db/db/dbRegion.cc | 2462 ++++++++++++++++++++++++++++++++++++----- src/db/db/dbRegion.h | 1992 +++++++++++++++++++++++++++++++-- 3 files changed, 4148 insertions(+), 324 deletions(-) diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 537a1e8ce..fae01830e 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -41,6 +41,18 @@ namespace db { class Edges; +/** + * @brief A base class for polygon filters + */ +class DB_PUBLIC EdgeFilterBase +{ +public: + EdgeFilterBase () { } + virtual ~EdgeFilterBase () { } + + virtual bool selected (const db::Edge &edge) const = 0; +}; + /** * @brief An edge length filter for use with Edges::filter or Edges::filtered * @@ -51,6 +63,7 @@ class Edges; */ struct DB_PUBLIC EdgeLengthFilter + : public EdgeFilterBase { typedef db::Edge::distance_type length_type; @@ -70,7 +83,7 @@ struct DB_PUBLIC EdgeLengthFilter /** * @brief Returns true if the edge length matches the criterion */ - bool operator() (const db::Edge &edge) const + bool selected (const db::Edge &edge) const { length_type l = edge.length (); if (! m_inverse) { @@ -96,6 +109,7 @@ private: */ struct DB_PUBLIC EdgeOrientationFilter + : public EdgeFilterBase { /** * @brief Constructor @@ -132,7 +146,7 @@ struct DB_PUBLIC EdgeOrientationFilter /** * @brief Returns true if the edge orientation matches the criterion */ - bool operator() (const db::Edge &edge) const + bool selected (const db::Edge &edge) const { int smin = db::vprod_sign (m_emin, db::DVector (edge.d ())); if (m_exact) { diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 65554fab4..4a2761b77 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -40,6 +40,2207 @@ namespace db { +// ------------------------------------------------------------------------------------------------------------- + +RegionDelegate::RegionDelegate () +{ + m_report_progress = false; + m_merged_semantics = true; + m_strict_handling = false; + m_merge_min_coherence = false; +} + +RegionDelegate::~RegionDelegate () +{ + // .. nothing yet .. +} + +void RegionDelegate::enable_progress (const std::string &progress_desc) +{ + m_report_progress = true; + m_progress_desc = progress_desc; +} + +void RegionDelegate::disable_progress () +{ + m_report_progress = false; +} + +void RegionDelegate::set_min_coherence (bool f) +{ + m_merge_min_coherence = f; +} + +void RegionDelegate::set_merged_semantics (bool f) +{ + if (f != m_merged_semantics) { + m_merged_semantics = f; + merged_semantics_changed (); + } +} + +void RegionDelegate::set_strict_handling (bool f) +{ + m_strict_handling = f; +} + +// ------------------------------------------------------------------------------------------------------------- +// EmptyRegion implementation + +EmptyRegion::EmptyRegion () +{ + // .. nothing yet .. +} + +EmptyRegion::~EmptyRegion () +{ + // .. nothing yet .. +} + +EmptyRegion::EmptyRegion (const EmptyRegion &other) + : RegionDelegate (other) +{ + // .. nothing yet .. +} + +RegionDelegate * +EmptyRegion::clone () const +{ + return new EmptyRegion (*this); +} + +RegionDelegate * +EmptyRegion::xor_with (const Region &other) const +{ + return other.delegate ()->clone (); +} + +RegionDelegate * +EmptyRegion::or_with (const Region &other) const +{ + return other.delegate ()->clone (); +} + +RegionDelegate * +EmptyRegion::add_in_place (const Region &other) +{ + return add (other); +} + +RegionDelegate * +EmptyRegion::add (const Region &other) const +{ + return other.delegate ()->clone (); +} + +// ------------------------------------------------------------------------------------------------------------- +// AsIfFlagRegion implementation + +AsIfFlatRegion::AsIfFlatRegion () + : RegionDelegate (), m_bbox_valid (false) +{ + // .. nothing yet .. +} + +AsIfFlatRegion::~AsIfFlatRegion () +{ + // .. nothing yet .. +} + +std::string +AsIfFlatRegion::to_string (size_t nmax) const +{ + std::ostringstream os; + RegionIterator p (begin ()); + bool first = true; + for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { + if (! first) { + os << ";"; + } + first = false; + os << p->to_string (); + } + if (! p.at_end ()) { + os << "..."; + } + return os.str (); +} + +Edges +AsIfFlatRegion::edges (const EdgeFilterBase *filter) const +{ + Edges edges; + + size_t n = 0; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + edges.reserve (n); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected (*e)) { + edges.insert (*e); + } + } + } + + return edges; +} + +RegionDelegate * +AsIfFlatRegion::hulls () const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + db::Polygon h; + h.assign_hull (p->begin_hull (), p->end_hull ()); + new_region->insert (h); + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::holes () const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + for (size_t i = 0; i < p->holes (); ++i) { + db::Polygon h; + h.assign_hull (p->begin_hole ((unsigned int) i), p->end_hole ((unsigned int) i)); + new_region->insert (h); + } + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::rounded_corners (double rinner, double router, unsigned int n) const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + new_region->insert (db::compute_rounded (*p, rinner, router, n)); + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::smoothed (coord_type d) const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + new_region->insert (db::smooth (*p, d)); + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::in (const Region &other, bool invert) const +{ + std::set op; + for (RegionIterator o (other.begin_merged ()); ! o.at_end (); ++o) { + op.insert (*o); + } + + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator o (begin_merged ()); ! o.at_end (); ++o) { + if ((op.find (*o) == op.end ()) == invert) { + new_region->insert (*o); + } + } + + return new_region.release (); +} + +bool +AsIfFlatRegion::is_box () const +{ + RegionIterator p (begin ()); + if (p.at_end ()) { + return false; + } else { + const db::Polygon &poly = *p; + ++p; + if (! p.at_end ()) { + return false; + } else { + return poly.is_box (); + } + } +} + +AsIfFlatRegion::area_type +AsIfFlatRegion::area (const db::Box &box) const +{ + area_type a = 0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (box.empty () || p->box ().inside (box)) { + a += p->area (); + } else { + std::vector clipped; + clip_poly (*p, box, clipped); + for (std::vector::const_iterator c = clipped.begin (); c != clipped.end (); ++c) { + a += c->area (); + } + } + } + + return a; +} + +AsIfFlatRegion::perimeter_type +AsIfFlatRegion::perimeter (const db::Box &box) const +{ + perimeter_type d = 0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + if (box.empty () || p->box ().inside (box)) { + d += p->perimeter (); + } else { + + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + + if (box.empty ()) { + d += (*e).length (); + } else { + + std::pair ce = (*e).clipped (box); + if (ce.first) { + + db::Coord dx = ce.second.dx (); + db::Coord dy = ce.second.dy (); + db::Coord x = ce.second.p1 ().x (); + db::Coord y = ce.second.p1 ().y (); + if ((dx == 0 && x == box.left () && dy < 0) || + (dx == 0 && x == box.right () && dy > 0) || + (dy == 0 && y == box.top () && dx < 0) || + (dy == 0 && y == box.bottom () && dx > 0)) { + // not counted -> box is at outside side of the edge + } else { + d += ce.second.length (); + } + + } + + } + + } + + } + + } + + return d; +} + +Box AsIfFlatRegion::bbox () const +{ + if (! m_bbox_valid) { + m_bbox = db::Box (); + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + m_bbox += p->box (); + } + m_bbox_valid = true; + } + + return m_bbox; +} + +void AsIfFlatRegion::update_bbox (const db::Box &b) +{ + m_bbox = b; + m_bbox_valid = true; +} + +RegionDelegate * +AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const +{ + std::auto_ptr new_region (new FlatRegion ()); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_region->insert (*p); + } + } + + return new_region.release (); +} + +namespace +{ + +/** + * @brief A helper class for the region to edge interaction functionality + * + * Note: This special scanner uses pointers to two different objects: edges and polygons. + * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate + * pointers to edges. + * + * There is a special box converter which is able to sort that out as well. + */ +template +class region_to_edge_interaction_filter + : public db::box_scanner_receiver +{ +public: + region_to_edge_interaction_filter (OutputContainer &output) + : mp_output (&output), m_inverse (false) + { + // .. nothing yet .. + } + + region_to_edge_interaction_filter (OutputContainer &output, const db::RegionIterator &polygons) + : mp_output (&output), m_inverse (true) + { + for (db::RegionIterator p = polygons; ! p.at_end (); ++p) { + m_seen.insert (&*p); + } + } + + void add (const char *o1, size_t p1, const char *o2, size_t p2) + { + const db::Edge *e = 0; + const db::Polygon *p = 0; + + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + if (p1 == 0 && p2 == 1) { + e = reinterpret_cast (o1); + p = reinterpret_cast (o2 - 1); + } else if (p1 == 1 && p2 == 0) { + e = reinterpret_cast (o2); + p = reinterpret_cast (o1 - 1); + } + + if (e && p && (m_seen.find (p) == m_seen.end ()) != m_inverse) { + + // A polygon and an edge interact if the edge is either inside completely + // of at least one edge of the polygon intersects with the edge + bool interacts = false; + if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) { + interacts = true; + } else { + for (db::Polygon::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) { + if ((*pe).intersect (*e)) { + interacts = true; + } + } + } + + if (interacts) { + if (m_inverse) { + m_seen.erase (p); + } else { + m_seen.insert (p); + mp_output->insert (*p); + } + } + + } + } + + void fill_output () + { + for (std::set::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) { + mp_output->insert (**p); + } + } + +private: + OutputContainer *mp_output; + std::set m_seen; + bool m_inverse; +}; + +/** + * @brief A special box converter that splits the pointers into polygon and edge pointers + */ +struct EdgeOrRegionBoxConverter +{ + typedef db::Box box_type; + + db::Box operator() (const char &c) const + { + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + const char *cp = &c; + if ((size_t (cp) & 1) == 1) { + // it's a polygon + return (reinterpret_cast (cp - 1))->box (); + } else { + // it's an edge + const db::Edge *e = reinterpret_cast (cp); + return db::Box (e->p1 (), e->p2 ()); + } + } +}; + +} + +RegionDelegate * +AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse) const +{ + if (other.empty ()) { + if (! inverse) { + return new EmptyRegion (); + } else { + return clone (); + } + } else if (empty ()) { + return clone (); + } + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + other.size ()); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + scanner.insert ((char *) &*p + 1, 1); + } + + other.ensure_valid_merged_edges (); + for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { + scanner.insert ((char *) &*e, 0); + } + + std::auto_ptr output (new FlatRegion (false)); + EdgeOrRegionBoxConverter bc; + + if (! inverse) { + region_to_edge_interaction_filter filter (output->raw_polygons ()); + scanner.process (filter, 1, bc); + } else { + region_to_edge_interaction_filter filter (output->raw_polygons (), RegionIterator (begin_merged ())); + scanner.process (filter, 1, bc); + filter.fill_output (); + } + + return output.release (); +} + +RegionDelegate * +AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const +{ + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // shortcut + if (empty ()) { + return clone (); + } else if (other.empty ()) { + // clear, if b is empty and + // * mode is inside or interacting and inverse is false ("inside" or "interacting") + // * mode is outside and inverse is true ("not outside") + if ((mode <= 0) != inverse) { + return new EmptyRegion (); + } else { + return clone (); + } + } + + for (RegionIterator p = other.begin (); ! p.at_end (); ++p) { + if (p->box ().touches (bbox ())) { + ep.insert (*p, 0); + } + } + + size_t n = 1; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) { + if (mode > 0 || p->box ().touches (other.bbox ())) { + ep.insert (*p, n); + } + } + + db::InteractionDetector id (mode, 0); + id.set_include_touching (touching); + db::EdgeSink es; + ep.process (es, id); + id.finish (); + + std::auto_ptr output (new FlatRegion (false)); + + n = 0; + std::set selected; + for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { + ++n; + selected.insert (i->second); + } + + output->reserve (n); + + n = 1; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) { + if ((selected.find (n) == selected.end ()) == inverse) { + output->raw_polygons ().insert (*p); + } + } + + return output.release (); +} + +EdgePairs +AsIfFlatRegion::grid_check (db::Coord gx, db::Coord gy) const +{ + EdgePairs out; + + gx = std::max (db::Coord (1), gx); + gy = std::max (db::Coord (1), gy); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + for (size_t i = 0; i < p->holes () + 1; ++i) { + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = p->begin_hull (); + e = p->end_hull (); + } else { + b = p->begin_hole ((unsigned int) (i - 1)); + e = p->end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + if (((*pt).x () % gx) != 0 || ((*pt).y () % gy) != 0) { + out.insert (EdgePair (db::Edge (*pt, *pt), db::Edge (*pt, *pt))); + } + } + + } + + } + + return out; +} + +static bool ac_less (double cos_a, bool gt180_a, double cos_b, bool gt180_b) +{ + if (gt180_a != gt180_b) { + return gt180_a < gt180_b; + } else { + if (gt180_a) { + return cos_a < cos_b - 1e-10; + } else { + return cos_a > cos_b + 1e-10; + } + } +} + +EdgePairs +AsIfFlatRegion::angle_check (double min, double max, bool inverse) const +{ + EdgePairs out; + + double cos_min = cos (std::max (0.0, std::min (360.0, min)) / 180.0 * M_PI); + double cos_max = cos (std::max (0.0, std::min (360.0, max)) / 180.0 * M_PI); + bool gt180_min = min > 180.0; + bool gt180_max = max > 180.0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + for (size_t i = 0; i < p->holes () + 1; ++i) { + + const db::Polygon::contour_type *h = 0; + if (i == 0) { + h = &p->hull (); + } else { + h = &p->hole ((unsigned int) (i - 1)); + } + + size_t np = h->size (); + + for (size_t j = 0; j < np; ++j) { + + db::Edge e ((*h) [j], (*h) [(j + 1) % np]); + db::Edge ee (e.p2 (), (*h) [(j + 2) % np]); + double le = e.double_length (); + double lee = ee.double_length (); + + double cos_a = -db::sprod (e, ee) / (le * lee); + bool gt180_a = db::vprod_sign (e, ee) > 0; + + if ((ac_less (cos_a, gt180_a, cos_max, gt180_max) && !ac_less (cos_a, gt180_a, cos_min, gt180_min)) == !inverse) { + out.insert (EdgePair (e, ee)); + } + + } + + } + + } + + return out; +} + +static inline db::Coord snap_to_grid (db::Coord c, db::Coord g) +{ + // This form of snapping always snaps g/2 to right/top. + if (c < 0) { + c = -g * ((-c + (g - 1) / 2) / g); + } else { + c = g * ((c + g / 2) / g); + } + return c; +} + +RegionDelegate * +AsIfFlatRegion::snapped (db::Coord gx, db::Coord gy) +{ + std::auto_ptr new_region (new FlatRegion (merged_semantics ())); + + gx = std::max (db::Coord (1), gx); + gy = std::max (db::Coord (1), gy); + + std::vector pts; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + db::Polygon pnew; + + for (size_t i = 0; i < p->holes () + 1; ++i) { + + pts.clear (); + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = p->begin_hull (); + e = p->end_hull (); + } else { + b = p->begin_hole ((unsigned int) (i - 1)); + e = p->end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + pts.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); + } + + if (i == 0) { + pnew.assign_hull (pts.begin (), pts.end ()); + } else { + pnew.insert_hole (pts.begin (), pts.end ()); + } + + } + + new_region->raw_polygons ().insert (pnew); + + } + + return new_region.release (); +} + +namespace +{ + /** + * @brief A helper class to implement the strange polygon detector + */ + struct StrangePolygonInsideFunc + { + inline bool operator() (int wc) const + { + return wc < 0 || wc > 1; + } + }; +} + +RegionDelegate * +AsIfFlatRegion::strange_polygon_check () const +{ + EdgeProcessor ep; + std::auto_ptr new_region (new FlatRegion (merged_semantics ())); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + + ep.clear (); + ep.insert (*p); + + StrangePolygonInsideFunc inside; + db::GenericMerge op (inside); + db::ShapeGenerator pc (new_region->raw_polygons (), false); + db::PolygonGenerator pg (pc, false, false); + ep.process (pg, op); + } + + return new_region.release (); +} + + +namespace { + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +class Edge2EdgeCheck + : public db::box_scanner_receiver +{ +public: + Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool different_polygons, bool requires_different_layers) + : mp_check (&check), mp_output (&output), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), + m_pass (0) + { + m_distance = check.distance (); + } + + bool prepare_next_pass () + { + ++m_pass; + + if (m_pass == 1) { + + if (! m_ep.empty ()) { + m_ep_discarded.resize (m_ep.size (), false); + return true; + } + + } else if (m_pass == 2) { + + std::vector::const_iterator d = m_ep_discarded.begin (); + std::vector::const_iterator ep = m_ep.begin (); + while (ep != m_ep.end ()) { + tl_assert (d != m_ep_discarded.end ()); + if (! *d) { + mp_output->insert (*ep); + } + ++d; + ++ep; + } + + } + + return false; + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + if (m_pass == 0) { + + // Overlap or inside checks require input from different layers + if ((! m_different_polygons || p1 != p2) && (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0)) { + + // ensure that the first check argument is of layer 1 and the second of + // layer 2 (unless both are of the same layer) + int l1 = int (p1 & size_t (1)); + int l2 = int (p2 & size_t (1)); + + db::EdgePair ep; + if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { + + // found a violation: store inside the local buffer for now. In the second + // pass we will eliminate those which are shielded completely. + size_t n = m_ep.size (); + m_ep.push_back (ep); + m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n)); + m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n)); + + } + + } + + } else { + + // a simple (complete) shielding implementation which is based on the + // assumption that shielding is relevant as soon as a foreign edge cuts through + // both of the edge pair's connecting edges. + + // TODO: this implementation does not take into account the nature of the + // EdgePair - because of "whole_edge" it may not reflect the part actually + // violating the distance. + + std::vector n1, n2; + + for (unsigned int p = 0; p < 2; ++p) { + + std::pair k (*o1, p1); + for (std::multimap, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) { + n1.push_back (i->second); + } + + std::sort (n1.begin (), n1.end ()); + + std::swap (o1, o2); + std::swap (p1, p2); + n1.swap (n2); + + } + + for (unsigned int p = 0; p < 2; ++p) { + + std::vector nn; + std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); + + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + if (! m_ep_discarded [*i]) { + db::EdgePair ep = m_ep [*i].normalized (); + if (db::Edge (ep.first ().p1 (), ep.second ().p2 ()).intersect (*o2) && + db::Edge (ep.second ().p1 (), ep.first ().p2 ()).intersect (*o2)) { + m_ep_discarded [*i] = true; + } + } + } + + std::swap (o1, o2); + std::swap (p1, p2); + n1.swap (n2); + + } + + } + + } + + /** + * @brief Gets a value indicating whether the check requires different layers + */ + bool requires_different_layers () const + { + return m_requires_different_layers; + } + + /** + * @brief Sets a value indicating whether the check requires different layers + */ + void set_requires_different_layers (bool f) + { + m_requires_different_layers = f; + } + + /** + * @brief Gets a value indicating whether the check requires different layers + */ + bool different_polygons () const + { + return m_different_polygons; + } + + /** + * @brief Sets a value indicating whether the check requires different layers + */ + void set_different_polygons (bool f) + { + m_different_polygons = f; + } + + /** + * @brief Gets the distance value + */ + EdgeRelationFilter::distance_type distance () const + { + return m_distance; + } + +private: + const EdgeRelationFilter *mp_check; + EdgePairs *mp_output; + bool m_requires_different_layers; + bool m_different_polygons; + EdgeRelationFilter::distance_type m_distance; + std::vector m_ep; + std::multimap, size_t> m_e2ep; + std::vector m_ep_discarded; + unsigned int m_pass; +}; + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +class Poly2PolyCheck + : public db::box_scanner_receiver +{ +public: + Poly2PolyCheck (Edge2EdgeCheck &output) + : mp_output (&output) + { + // .. nothing yet .. + } + + void finish (const db::Polygon *o, size_t p) + { + if (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()) { + + // finally we check the polygons vs. itself for checks involving intra-polygon interactions + + m_scanner.clear (); + m_scanner.reserve (o->vertices ()); + + m_edges.clear (); + m_edges.reserve (o->vertices ()); + + for (db::Polygon::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p); + } + + tl_assert (m_edges.size () == o->vertices ()); + + m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); + + } + } + + void add (const db::Polygon *o1, size_t p1, const db::Polygon *o2, size_t p2) + { + if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { + + m_scanner.clear (); + m_scanner.reserve (o1->vertices () + o2->vertices ()); + + m_edges.clear (); + m_edges.reserve (o1->vertices () + o2->vertices ()); + + for (db::Polygon::polygon_edge_iterator e = o1->begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p1); + } + + for (db::Polygon::polygon_edge_iterator e = o2->begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p2); + } + + tl_assert (m_edges.size () == o1->vertices () + o2->vertices ()); + + // temporarily disable intra-polygon check in that step .. we do that later in finish() + // if required (#650). + bool no_intra = mp_output->different_polygons (); + mp_output->set_different_polygons (true); + + m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); + + mp_output->set_different_polygons (no_intra); + + } + } + +private: + db::box_scanner m_scanner; + Edge2EdgeCheck *mp_output; + std::vector m_edges; +}; + +} + +EdgePairs +AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + EdgePairs result; + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + (other ? other->size () : 0)); + + size_t n = 0; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + scanner.insert (&*p, n); + n += 2; + } + + if (other) { + n = 1; + for (RegionIterator p (other->begin_merged ()); ! p.at_end (); ++p) { + scanner.insert (&*p, n); + n += 2; + } + } + + EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (other != 0); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + Edge2EdgeCheck edge_check (check, result, different_polygons, other != 0); + Poly2PolyCheck poly_check (edge_check); + + do { + scanner.process (poly_check, d, db::box_convert ()); + } while (edge_check.prepare_next_pass ()); + + return result; +} + +EdgePairs +AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + EdgePairs result; + + EdgeRelationFilter check (rel, d, metrics); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + Edge2EdgeCheck edge_check (check, result, false, false); + Poly2PolyCheck poly_check (edge_check); + + do { + size_t n = 0; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + poly_check.finish (&*p, n); + n += 2; + } + } while (edge_check.prepare_next_pass ()); + + return result; +} + +RegionDelegate * +AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc) const +{ + if (empty ()) { + + return new EmptyRegion (); + + } else if (is_box ()) { + + // take box only if min_wc == 0, otherwise clear + if (min_wc > 0) { + return new EmptyRegion (); + } else { + return clone (); + } + + } else { + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + + // and run the merge step + db::MergeOp op (min_wc); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::region_from_box (const db::Box &b) +{ + if (! b.empty () && b.width () > 0 && b.height () > 0) { + FlatRegion *new_region = new FlatRegion (); + new_region->insert (b); + return new_region; + } else { + return new EmptyRegion (); + } +} + +RegionDelegate * +AsIfFlatRegion::sized (coord_type d, unsigned int mode) const +{ + return sized (d, d, mode); +} + +RegionDelegate * +AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const +{ + if (empty ()) { + + // ignore empty + return new EmptyRegion (); + + } else if (is_box () && mode >= 2) { + + // simplified handling for a box + db::Box b = bbox ().enlarged (db::Vector (dx, dy)); + return region_from_box (b); + + } else if (! merged_semantics ()) { + + // Generic case + std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); + + db::ShapeGenerator pc (new_region->raw_polygons (), false); + db::PolygonGenerator pg (pc, false, true); + db::SizingPolygonFilter sf (pg, dx, dy, mode); + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + sf.put (*p); + } + + return new_region.release (); + + } else { + + // Generic case - the size operation will merge first + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); + db::SizingPolygonFilter siz (pg2, dx, dy, mode); + db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/); + db::BooleanOp op (db::BooleanOp::Or); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::and_with (const Region &other) const +{ + if (empty () || other.empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (is_box () && other.is_box ()) { + + // Simplified handling for boxes + db::Box b = bbox (); + b &= other.bbox (); + return region_from_box (b); + + } else if (is_box () && ! other.strict_handling ()) { + + // map AND with box to clip .. + db::Box b = bbox (); + std::auto_ptr new_region (new FlatRegion ()); + + std::vector clipped; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + clipped.clear (); + clip_poly (*p, b, clipped); + new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + } + + return new_region.release (); + + } else if (other.is_box () && ! strict_handling ()) { + + // map AND with box to clip .. + db::Box b = other.bbox (); + std::auto_ptr new_region (new FlatRegion ()); + + std::vector clipped; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + clipped.clear (); + clip_poly (*p, b, clipped); + new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + } + + return new_region.release (); + + } else if (! bbox ().overlaps (other.bbox ())) { + + // Result will be nothing + return new EmptyRegion (); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::And); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::not_with (const Region &other) const +{ + if (empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (other.empty () && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::ANotB); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::xor_with (const Region &other) const +{ + if (empty () && ! other.strict_handling ()) { + + return other.delegate ()->clone (); + + } else if (other.empty () && ! strict_handling ()) { + + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { + + // Simplified handling for disjunct case + return or_with (other); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::Xor); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::or_with (const Region &other) const +{ + if (empty () && ! other.strict_handling ()) { + + return other.delegate ()->clone (); + + } else if (other.empty () && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { + + // Simplified handling for disjunct case + return add (other); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::Or); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::add (const Region &other) const +{ + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + std::auto_ptr new_region (new FlatRegion (*other_flat)); + + size_t n = new_region->raw_polygons ().size () + size (); + + new_region->reserve (n); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + return new_region.release (); + + } else { + + std::auto_ptr new_region (new FlatRegion (false /*not merged*/)); + + size_t n = size () + other.size (); + + new_region->reserve (n); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + return new_region.release (); + + } +} + +bool +AsIfFlatRegion::equals (const Region &other) const +{ + if (empty () != other.empty ()) { + return false; + } + if (size () != other.size ()) { + return false; + } + RegionIterator o1 (begin ()); + RegionIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return false; + } + ++o1; + ++o2; + } + return true; +} + +bool +AsIfFlatRegion::less (const Region &other) const +{ + if (empty () != other.empty ()) { + return empty () < other.empty (); + } + if (size () != other.size ()) { + return (size () < other.size ()); + } + RegionIterator o1 (begin ()); + RegionIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return *o1 < *o2; + } + ++o1; + ++o2; + } + return false; +} + +// ------------------------------------------------------------------------------------------------------------- +// FlatRegion implementation + +namespace +{ + + class FlatRegionIterator + : public RegionIteratorDelegate + { + public: + typedef db::layer polygon_layer_type; + typedef polygon_layer_type::iterator iterator_type; + + FlatRegionIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // .. nothing yet .. + } + + virtual bool at_end () const + { + return m_from == m_to; + } + + virtual void increment () + { + ++m_from; + } + + virtual const value_type *get () const + { + return m_from.operator-> (); + } + + virtual RegionIteratorDelegate *clone () const + { + return new FlatRegionIterator (*this); + } + + private: + friend class Region; + + iterator_type m_from, m_to; + }; + +} + + +FlatRegion::FlatRegion () + : AsIfFlatRegion () +{ + init (); +} + +FlatRegion::~FlatRegion () +{ + // .. nothing yet .. +} + +FlatRegion::FlatRegion (const FlatRegion &other) + : AsIfFlatRegion (other), m_polygons (false), m_merged_polygons (false) +{ + init (); + + m_is_merged = other.m_is_merged; + m_polygons = other.m_polygons; + m_merged_polygons = other.m_merged_polygons; + m_merged_polygons_valid = other.m_merged_polygons_valid; +} + +FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) + : AsIfFlatRegion (), m_polygons (polygons), m_merged_polygons (false) +{ + init (); + + m_is_merged = is_merged; +} + +FlatRegion::FlatRegion (bool is_merged) + : AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false) +{ + init (); + + m_is_merged = is_merged; +} + +void FlatRegion::invalidate_cache () +{ + invalidate_bbox (); + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +void FlatRegion::init () +{ + m_is_merged = true; + m_merged_polygons_valid = false; +} + +void FlatRegion::merged_semantics_changed () +{ + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +void FlatRegion::reserve (size_t n) +{ + m_polygons.reserve (db::Polygon::tag (), n); +} + +void +FlatRegion::ensure_merged_polygons_valid () const +{ + if (! m_merged_polygons_valid) { + + m_merged_polygons.clear (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (0); + db::ShapeGenerator pc (m_merged_polygons); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + m_merged_polygons_valid = true; + + } +} + +RegionIteratorDelegate *FlatRegion::begin () const +{ + return new FlatRegionIterator (m_polygons.get_layer ().begin (), m_polygons.get_layer ().end ()); +} + +RegionIteratorDelegate *FlatRegion::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_polygons_valid (); + return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); + } +} + +std::pair FlatRegion::begin_iter () const +{ + return std::make_pair (db::RecursiveShapeIterator (m_polygons), db::ICplxTrans ()); +} + +std::pair FlatRegion::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_polygons_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); + } +} + +bool FlatRegion::empty () const +{ + return m_polygons.empty (); +} + +size_t FlatRegion::size () const +{ + return m_polygons.size (); +} + +bool FlatRegion::is_merged () const +{ + return m_is_merged; +} + +void FlatRegion::set_box (const db::Box &b) +{ + m_polygons.clear (); + if (! b.empty () && b.width () > 0 && b.height () > 0 ) { + m_polygons.insert (db::Polygon (b)); + } + + m_is_merged = true; + update_bbox (b); + + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter) +{ + polygon_iterator_type pw = m_polygons.get_layer ().begin (); + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + if (pw == m_polygons.get_layer ().end ()) { + m_polygons.get_layer ().insert (*p); + pw = m_polygons.get_layer ().end (); + } else { + m_polygons.get_layer ().replace (pw++, *p); + } + } + } + + m_polygons.get_layer ().erase (pw, m_polygons.get_layer ().end ()); + m_merged_polygons.clear (); + m_is_merged = merged_semantics (); + + return this; +} + +RegionDelegate *FlatRegion::merged_in_place () +{ + if (! m_is_merged) { + + if (m_merged_polygons_valid) { + + m_polygons.swap (m_merged_polygons); + m_merged_polygons.clear (); + m_is_merged = true; + return this; + + } else { + return merged_in_place (min_coherence (), 0); + } + + } else { + return this; + } +} + +RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int min_wc) +{ + if (empty ()) { + + // ignore empty + return new EmptyRegion (); + + } else if (is_box ()) { + + // take box only if min_wc == 0, otherwise clear + if (min_wc > 0) { + return new EmptyRegion (); + } + + } else { + + invalidate_cache (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (min_wc); + db::ShapeGenerator pc (m_polygons, true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + m_is_merged = true; + + } + + return this; +} + +RegionDelegate *FlatRegion::merged () const +{ + if (! m_is_merged) { + + if (m_merged_polygons_valid) { + return new FlatRegion (m_merged_polygons, true); + } else { + return AsIfFlatRegion::merged (min_coherence (), 0); + } + + } else { + return clone (); + } +} + +RegionDelegate *FlatRegion::add (const Region &other) const +{ + std::auto_ptr new_region (new FlatRegion (*this)); + + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + new_region->raw_polygons ().insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); + + } else { + + size_t n = new_region->raw_polygons ().size (); + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + new_region->raw_polygons ().reserve (db::Polygon::tag (), n); + + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + } + + return new_region.release (); +} + +RegionDelegate *FlatRegion::add_in_place (const Region &other) +{ + invalidate_cache (); + + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + m_polygons.insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); + + } else { + + size_t n = m_polygons.size (); + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + m_polygons.reserve (db::Polygon::tag (), n); + + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + m_polygons.insert (*p); + } + + } + + m_is_merged = false; + + return this; +} + +const db::Polygon *FlatRegion::nth (size_t n) const +{ + return n < m_polygons.size () ? &m_polygons.get_layer ().begin () [n] : 0; +} + +bool FlatRegion::has_valid_polygons () const +{ + return true; +} + +const db::RecursiveShapeIterator *FlatRegion::iter () const +{ + return 0; +} + +void +FlatRegion::insert (const db::Box &box) +{ + if (! box.empty () && box.width () > 0 && box.height () > 0) { + m_polygons.insert (db::Polygon (box)); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::Path &path) +{ + if (path.points () > 0) { + m_polygons.insert (path.polygon ()); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::Polygon &polygon) +{ + if (polygon.holes () > 0 || polygon.vertices () > 0) { + m_polygons.insert (polygon); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::SimplePolygon &polygon) +{ + if (polygon.vertices () > 0) { + db::Polygon poly; + poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); + m_polygons.insert (poly); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::Shape &shape) +{ + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon poly; + shape.polygon (poly); + m_polygons.insert (poly); + m_is_merged = false; + invalidate_cache (); + } +} + +// ------------------------------------------------------------------------------------------------------------- + +#if 0 // @@@ ORIGINAL +/** + * @brief A region iterator + * + * The iterator delivers the polygons of the region + */ + +class DB_PUBLIC RegionIterator +{ +public: + typedef db::Polygon value_type; + typedef const db::Polygon &reference; + typedef const db::Polygon *pointer; + typedef std::forward_iterator_tag iterator_category; + typedef void difference_type; + + /** + * @Returns true, if the iterator is at the end + */ + bool at_end () const + { + return m_from == m_to && m_rec_iter.at_end (); + } + + /** + * @brief Increment + */ + RegionIterator &operator++ () + { + inc (); + set (); + return *this; + } + + /** + * @brief Access + */ + reference operator* () const + { + if (m_rec_iter.at_end ()) { + return *m_from; + } else { + return m_polygon; + } + } + + /** + * @brief Access + */ + pointer operator-> () const + { + if (m_rec_iter.at_end ()) { + return &*m_from; + } else { + return &m_polygon; + } + } + +private: + friend class Region; + + typedef db::layer polygon_layer_type; + typedef polygon_layer_type::iterator iterator_type; + + db::RecursiveShapeIterator m_rec_iter; + db::ICplxTrans m_iter_trans; + db::Polygon m_polygon; + iterator_type m_from, m_to; + + /** + * @brief ctor from a recursive shape iterator + */ + RegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) + : m_rec_iter (iter), m_iter_trans (trans), m_from (), m_to () + { + // NOTE: the following initialization appears to be required on some compilers + // (specifically MacOS/clang) to ensure the proper initialization of the iterators + m_from = m_to; + set (); + } + + /** + * @brief ctor from a range of polygons inside a vector + */ + RegionIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // no required yet: set (); + } + + /** + * @brief Establish the iterator at the current position + */ + void set () + { + while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { + inc (); + } + if (! m_rec_iter.at_end ()) { + m_rec_iter.shape ().polygon (m_polygon); + m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); + } + } + + /** + * @brief Increment the iterator + */ + void inc () + { + if (! m_rec_iter.at_end ()) { + ++m_rec_iter; + } else { + ++m_from; + } + } +}; +#endif + +#if 0 // @@@ TODO +// .......................................................... + + + +/** + * @brief An original layerregion based on a RecursiveShapeIterator + */ +class DB_PUBLIC OriginalLayerRegion + : public RegionDelegate +{ +public: + OriginalLayerRegion () { } + virtual ~OriginalLayerRegion () { } + + RegionDelegate *clone () const; + + virtual void enable_progress (const std::string &progress_desc); + virtual void disable_progress (); + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + + virtual bool is_box () const; + virtual bool is_merged () const; + virtual area_type area (const db::Box &box = db::Box ()) const; + virtual perimeter_type perimeter (const db::Box &box = db::Box ()) const; + + virtual Box bbox () const; + + virtual EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const; + virtual EdgePairs angle_check (double min, double max, bool inverse) const; + virtual void snap (db::Coord gx, db::Coord gy); + virtual Region strange_polygon_check () const; + + virtual Edges edges () const; + + virtual void transform (const db::ICplxTrans &trans); + virtual void transform (const db::Trans &trans); + virtual void merge (); + virtual void merge (bool min_coherence, unsigned int min_wc); + + virtual RegionDelegate *sized (coord_type d, unsigned int mode); + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode); + + virtual RegionDelegate *and_with (const Region &other) const; + virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &other) const; + virtual RegionDelegate *selected_not_outside (const Region &other) const; + virtual RegionDelegate *selected_inside (const Region &other) const; + virtual RegionDelegate *selected_not_inside (const Region &other) const; + virtual RegionDelegate *selected_interacting (const Region &other) const; + virtual RegionDelegate *selected_not_interacting (const Region &other) const; + virtual RegionDelegate *selected_interacting (const Edges &other) const; + virtual RegionDelegate *selected_not_interacting (const Edges &other) const; + virtual RegionDelegate *selected_overlapping (const Region &other) const; + virtual RegionDelegate *selected_not_overlapping (const Region &other) const; + + virtual RegionDelegate *holes () const; + virtual RegionDelegate *hulls () const; + virtual RegionDelegate *in (const Region &other, bool invert) const; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; + virtual RegionDelegate *smoothed (coord_type d) const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; +}; + +#endif + +// ------------------------------------------------------------------------------------------------------------- +// Region implementation + +Region::Region () + : mp_delegate (new EmptyRegion ()) +{ + // .. nothing yet .. +} + +Region::Region (RegionDelegate *delegate) + : mp_delegate (delegate) +{ + // .. nothing yet .. +} + +Region::Region (const Region &other) + : mp_delegate (other.mp_delegate->clone ()) +{ + // .. nothing yet .. +} + +Region::~Region () +{ + delete mp_delegate; + mp_delegate = 0; +} + +Region &Region::operator= (const Region &other) +{ + if (this != &other) { + set_delegate (other.mp_delegate->clone ()); + } + return *this; +} + +Region::Region (const RecursiveShapeIterator &si) +{ + mp_delegate = new OriginalLayerRegion (si); +} + +Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics) +{ + mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics); +} + +const db::RecursiveShapeIterator & +Region::iter () const +{ + static db::RecursiveShapeIterator def_iter; + const db::RecursiveShapeIterator *i = mp_delegate->iter (); + return *(i ? i : &def_iter); +} + +// ------------------------------------------------------------------------------------------------------------- + +#if 0 +@@@@ original + Region::Region (const RecursiveShapeIterator &si) : m_polygons (false), m_merged_polygons (false), m_iter (si) { @@ -155,7 +2356,7 @@ Region::to_string (size_t nmax) const return os.str (); } -Region::area_type +Region::area_type Region::area (const db::Box &box) const { area_type a = 0; @@ -1066,7 +3267,6 @@ Region::selected_interacting_generic (const Edges &other, bool inverse) const db::box_scanner scanner (m_report_progress, m_progress_desc); scanner.reserve (size () + other.size ()); - ensure_valid_polygons (); for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { scanner.insert ((char *) &*p + 1, 1); } @@ -1107,7 +3307,6 @@ Region::select_interacting_generic (const Edges &other, bool inverse) db::box_scanner scanner (m_report_progress, m_progress_desc); scanner.reserve (size () + other.size ()); - ensure_valid_polygons (); for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { scanner.insert ((char *) &*p + 1, 1); } @@ -1546,260 +3745,6 @@ Region::clear () m_iter_trans = db::ICplxTrans (); } -namespace { - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - */ -class Edge2EdgeCheck - : public db::box_scanner_receiver -{ -public: - Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool different_polygons, bool requires_different_layers) - : mp_check (&check), mp_output (&output), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), - m_pass (0) - { - m_distance = check.distance (); - } - - bool prepare_next_pass () - { - ++m_pass; - - if (m_pass == 1) { - - if (! m_ep.empty ()) { - m_ep_discarded.resize (m_ep.size (), false); - return true; - } - - } else if (m_pass == 2) { - - std::vector::const_iterator d = m_ep_discarded.begin (); - std::vector::const_iterator ep = m_ep.begin (); - while (ep != m_ep.end ()) { - tl_assert (d != m_ep_discarded.end ()); - if (! *d) { - mp_output->insert (*ep); - } - ++d; - ++ep; - } - - } - - return false; - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - if (m_pass == 0) { - - // Overlap or inside checks require input from different layers - if ((! m_different_polygons || p1 != p2) && (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0)) { - - // ensure that the first check argument is of layer 1 and the second of - // layer 2 (unless both are of the same layer) - int l1 = int (p1 & size_t (1)); - int l2 = int (p2 & size_t (1)); - - db::EdgePair ep; - if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { - - // found a violation: store inside the local buffer for now. In the second - // pass we will eliminate those which are shielded completely. - size_t n = m_ep.size (); - m_ep.push_back (ep); - m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n)); - m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n)); - - } - - } - - } else { - - // a simple (complete) shielding implementation which is based on the - // assumption that shielding is relevant as soon as a foreign edge cuts through - // both of the edge pair's connecting edges. - - // TODO: this implementation does not take into account the nature of the - // EdgePair - because of "whole_edge" it may not reflect the part actually - // violating the distance. - - std::vector n1, n2; - - for (unsigned int p = 0; p < 2; ++p) { - - std::pair k (*o1, p1); - for (std::multimap, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) { - n1.push_back (i->second); - } - - std::sort (n1.begin (), n1.end ()); - - std::swap (o1, o2); - std::swap (p1, p2); - n1.swap (n2); - - } - - for (unsigned int p = 0; p < 2; ++p) { - - std::vector nn; - std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); - - for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { - if (! m_ep_discarded [*i]) { - db::EdgePair ep = m_ep [*i].normalized (); - if (db::Edge (ep.first ().p1 (), ep.second ().p2 ()).intersect (*o2) && - db::Edge (ep.second ().p1 (), ep.first ().p2 ()).intersect (*o2)) { - m_ep_discarded [*i] = true; - } - } - } - - std::swap (o1, o2); - std::swap (p1, p2); - n1.swap (n2); - - } - - } - - } - - /** - * @brief Gets a value indicating whether the check requires different layers - */ - bool requires_different_layers () const - { - return m_requires_different_layers; - } - - /** - * @brief Sets a value indicating whether the check requires different layers - */ - void set_requires_different_layers (bool f) - { - m_requires_different_layers = f; - } - - /** - * @brief Gets a value indicating whether the check requires different layers - */ - bool different_polygons () const - { - return m_different_polygons; - } - - /** - * @brief Sets a value indicating whether the check requires different layers - */ - void set_different_polygons (bool f) - { - m_different_polygons = f; - } - - /** - * @brief Gets the distance value - */ - EdgeRelationFilter::distance_type distance () const - { - return m_distance; - } - -private: - const EdgeRelationFilter *mp_check; - EdgePairs *mp_output; - bool m_requires_different_layers; - bool m_different_polygons; - EdgeRelationFilter::distance_type m_distance; - std::vector m_ep; - std::multimap, size_t> m_e2ep; - std::vector m_ep_discarded; - unsigned int m_pass; -}; - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - */ -class Poly2PolyCheck - : public db::box_scanner_receiver -{ -public: - Poly2PolyCheck (Edge2EdgeCheck &output) - : mp_output (&output) - { - // .. nothing yet .. - } - - void finish (const db::Polygon *o, size_t p) - { - if (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()) { - - // finally we check the polygons vs. itself for checks involving intra-polygon interactions - - m_scanner.clear (); - m_scanner.reserve (o->vertices ()); - - m_edges.clear (); - m_edges.reserve (o->vertices ()); - - for (db::Polygon::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p); - } - - tl_assert (m_edges.size () == o->vertices ()); - - m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); - - } - } - - void add (const db::Polygon *o1, size_t p1, const db::Polygon *o2, size_t p2) - { - if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { - - m_scanner.clear (); - m_scanner.reserve (o1->vertices () + o2->vertices ()); - - m_edges.clear (); - m_edges.reserve (o1->vertices () + o2->vertices ()); - - for (db::Polygon::polygon_edge_iterator e = o1->begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p1); - } - - for (db::Polygon::polygon_edge_iterator e = o2->begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p2); - } - - tl_assert (m_edges.size () == o1->vertices () + o2->vertices ()); - - // temporarily disable intra-polygon check in that step .. we do that later in finish() - // if required (#650). - bool no_intra = mp_output->different_polygons (); - mp_output->set_different_polygons (true); - - m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); - - mp_output->set_different_polygons (no_intra); - - } - } - -private: - db::box_scanner m_scanner; - Edge2EdgeCheck *mp_output; - std::vector m_edges; -}; - -} - EdgePairs Region::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const { @@ -1866,6 +3811,9 @@ Region::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool return result; } +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@q +#endif + } namespace tl diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 80e4b08de..9346cb7aa 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -43,6 +43,20 @@ namespace db { +class EdgeFilterBase; + +/** + * @brief A base class for polygon filters + */ +class DB_PUBLIC PolygonFilterBase +{ +public: + PolygonFilterBase () { } + virtual ~PolygonFilterBase () { } + + virtual bool selected (const db::Polygon &polgon) const = 0; +}; + /** * @brief A perimeter filter for use with Region::filter or Region::filtered * @@ -53,6 +67,7 @@ namespace db { */ struct DB_PUBLIC RegionPerimeterFilter + : public PolygonFilterBase { typedef db::coord_traits::perimeter_type perimeter_type; @@ -72,7 +87,7 @@ struct DB_PUBLIC RegionPerimeterFilter /** * @brief Returns true if the polygon's perimeter matches the criterion */ - bool operator() (const db::Polygon &poly) const + virtual bool selected (const db::Polygon &poly) const { perimeter_type p = 0; for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end () && p < m_pmax; ++e) { @@ -101,6 +116,7 @@ private: */ struct DB_PUBLIC RegionAreaFilter + : public PolygonFilterBase { typedef db::Polygon::area_type area_type; @@ -120,7 +136,7 @@ struct DB_PUBLIC RegionAreaFilter /** * @brief Returns true if the polygon's area matches the criterion */ - bool operator() (const db::Polygon &poly) const + virtual bool selected (const db::Polygon &poly) const { area_type a = poly.area (); if (! m_inverse) { @@ -142,6 +158,7 @@ private: */ struct DB_PUBLIC RectilinearFilter + : public PolygonFilterBase { /** * @brief Constructor @@ -156,7 +173,7 @@ struct DB_PUBLIC RectilinearFilter /** * @brief Returns true if the polygon's area matches the criterion */ - bool operator() (const db::Polygon &poly) const + virtual bool selected (const db::Polygon &poly) const { return poly.is_rectilinear () != m_inverse; } @@ -172,6 +189,7 @@ private: */ struct DB_PUBLIC RectangleFilter + : public PolygonFilterBase { /** * @brief Constructor @@ -186,7 +204,7 @@ struct DB_PUBLIC RectangleFilter /** * @brief Returns true if the polygon's area matches the criterion */ - bool operator() (const db::Polygon &poly) const + virtual bool selected (const db::Polygon &poly) const { return poly.is_box () != m_inverse; } @@ -212,6 +230,7 @@ private: */ struct DB_PUBLIC RegionBBoxFilter + : public PolygonFilterBase { typedef db::Box::distance_type value_type; @@ -242,7 +261,7 @@ struct DB_PUBLIC RegionBBoxFilter /** * @brief Returns true if the polygon's area matches the criterion */ - bool operator() (const db::Polygon &poly) const + virtual bool selected (const db::Polygon &poly) const { value_type v = 0; db::Box box = poly.box (); @@ -270,36 +289,102 @@ private: parameter_type m_parameter; }; +/** + * @brief The region iterator delegate + */ +class DB_PUBLIC RegionIteratorDelegate +{ +public: + RegionIteratorDelegate () { } + virtual ~RegionIteratorDelegate () { } + + typedef db::Polygon value_type; + + virtual bool at_end () const = 0; + virtual void increment () = 0; + virtual const value_type *get () const = 0; + virtual RegionIteratorDelegate *clone () const = 0; +}; + /** * @brief A region iterator * * The iterator delivers the polygons of the region */ - class DB_PUBLIC RegionIterator { public: - typedef db::Polygon value_type; - typedef const db::Polygon &reference; - typedef const db::Polygon *pointer; + typedef RegionIteratorDelegate::value_type value_type; + typedef const value_type &reference; + typedef const value_type *pointer; typedef std::forward_iterator_tag iterator_category; typedef void difference_type; + /** + * @brief Default constructor + */ + RegionIterator () + : mp_delegate (0) + { + // .. nothing yet .. + } + + /** + * @brief Constructor from a delegate + * The iterator will take ownership over the delegate + */ + RegionIterator (RegionIteratorDelegate *delegate) + : mp_delegate (delegate) + { + // .. nothing yet .. + } + + /** + * @brief Destructor + */ + ~RegionIterator () + { + delete mp_delegate; + mp_delegate = 0; + } + + /** + * @brief Copy constructor and assignment + */ + RegionIterator (const RegionIterator &other) + : mp_delegate (0) + { + operator= (other); + } + + /** + * @brief Assignment + */ + RegionIterator &operator= (const RegionIterator &other) + { + if (this != &other) { + delete mp_delegate; + mp_delegate = other.mp_delegate ? other.mp_delegate->clone () : 0; + } + return *this; + } + /** * @Returns true, if the iterator is at the end */ bool at_end () const { - return m_from == m_to && m_rec_iter.at_end (); + return mp_delegate == 0 || mp_delegate->at_end (); } /** * @brief Increment */ - RegionIterator &operator++ () + RegionIterator &operator++ () { - inc (); - set (); + if (mp_delegate) { + mp_delegate->increment (); + } return *this; } @@ -308,11 +393,9 @@ public: */ reference operator* () const { - if (m_rec_iter.at_end ()) { - return *m_from; - } else { - return m_polygon; - } + const value_type *value = operator-> (); + tl_assert (value != 0); + return *value; } /** @@ -320,72 +403,1851 @@ public: */ pointer operator-> () const { - if (m_rec_iter.at_end ()) { - return &*m_from; - } else { - return &m_polygon; - } + return mp_delegate ? mp_delegate->get () : 0; } private: - friend class Region; + RegionIteratorDelegate *mp_delegate; +}; +/** + * @brief The delegate for the actual region implementation + */ +class DB_PUBLIC RegionDelegate +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::Polygon polygon_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type distance_type; + typedef coord_traits::perimeter_type perimeter_type; + typedef coord_traits::area_type area_type; + + RegionDelegate (); + virtual ~RegionDelegate (); + + virtual RegionDelegate *clone () const = 0; + + void enable_progress (const std::string &progress_desc); + void disable_progress (); + + void set_min_coherence (bool f); + bool min_coherence () const + { + return m_merge_min_coherence; + } + + void set_merged_semantics (bool f); + bool merged_semantics () const + { + return m_merged_semantics; + } + + void set_strict_handling (bool f); + bool strict_handling () const + { + return m_strict_handling; + } + + virtual std::string to_string (size_t nmax) const = 0; + + virtual RegionIteratorDelegate *begin () const = 0; + virtual RegionIteratorDelegate *begin_merged () const = 0; + + virtual std::pair begin_iter () const = 0; + virtual std::pair begin_merged_iter () const = 0; + + virtual bool empty () const = 0; + virtual bool is_box () const = 0; + virtual bool is_merged () const = 0; + virtual size_t size () const = 0; + + virtual area_type area (const db::Box &box) const = 0; + virtual perimeter_type perimeter (const db::Box &box) const = 0; + virtual Box bbox () const = 0; + + virtual EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const = 0; + virtual EdgePairs angle_check (double min, double max, bool inverse) const = 0; + + virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) = 0; + virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy) = 0; + + virtual Edges edges (const EdgeFilterBase *filter) const = 0; + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) = 0; + virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0; + + virtual RegionDelegate *merged_in_place () = 0; + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc) = 0; + virtual RegionDelegate *merged () const = 0; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const = 0; + + virtual RegionDelegate *strange_polygon_check () const = 0; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const = 0; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const = 0; + + virtual RegionDelegate *and_with (const Region &other) const = 0; + virtual RegionDelegate *not_with (const Region &other) const = 0; + virtual RegionDelegate *xor_with (const Region &other) const = 0; + virtual RegionDelegate *or_with (const Region &other) const = 0; + virtual RegionDelegate *add_in_place (const Region &other) = 0; + virtual RegionDelegate *add (const Region &other) const = 0; + + virtual RegionDelegate *selected_outside (const Region &other) const = 0; + virtual RegionDelegate *selected_not_outside (const Region &other) const = 0; + virtual RegionDelegate *selected_inside (const Region &other) const = 0; + virtual RegionDelegate *selected_not_inside (const Region &other) const = 0; + virtual RegionDelegate *selected_interacting (const Region &other) const = 0; + virtual RegionDelegate *selected_not_interacting (const Region &other) const = 0; + virtual RegionDelegate *selected_interacting (const Edges &other) const = 0; + virtual RegionDelegate *selected_not_interacting (const Edges &other) const = 0; + virtual RegionDelegate *selected_overlapping (const Region &other) const = 0; + virtual RegionDelegate *selected_not_overlapping (const Region &other) const = 0; + + virtual RegionDelegate *holes () const = 0; + virtual RegionDelegate *hulls () const = 0; + virtual RegionDelegate *in (const Region &other, bool invert) const = 0; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const = 0; + virtual RegionDelegate *smoothed (coord_type d) const = 0; + + virtual const db::Polygon *nth (size_t n) const = 0; + virtual bool has_valid_polygons () const = 0; + + virtual const db::RecursiveShapeIterator *iter () const = 0; + + virtual bool equals (const Region &other) const = 0; + virtual bool less (const Region &other) const = 0; + +protected: + const std::string &progress_desc () const + { + return m_progress_desc; + } + + bool report_progress () const + { + return m_report_progress; + } + + virtual void merged_semantics_changed () { } + +private: + bool m_merged_semantics; + bool m_strict_handling; + bool m_merge_min_coherence; + bool m_report_progress; + std::string m_progress_desc; +}; + +/** + * @brief An empty Region + */ +class DB_PUBLIC EmptyRegion + : public RegionDelegate +{ +public: + EmptyRegion (); + virtual ~EmptyRegion (); + + EmptyRegion (const EmptyRegion &other); + RegionDelegate *clone () const; + + virtual RegionIteratorDelegate *begin () const { return 0; } + virtual RegionIteratorDelegate *begin_merged () const { return 0; } + + virtual std::pair begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + virtual std::pair begin_merged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + + virtual bool empty () const { return true; } + virtual size_t size () const { return 0; } + virtual std::string to_string (size_t) const { return std::string (); } + + virtual bool is_box () const { return false; } + virtual bool is_merged () const { return true; } + virtual area_type area (const db::Box &) const { return 0; } + virtual perimeter_type perimeter (const db::Box &) const { return 0; } + + virtual Box bbox () const { return Box (); } + + virtual EdgePairs width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs isolated_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs notch_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs separation_check (const Region &, db::Coord, bool , metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs inside_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs grid_check (db::Coord, db::Coord) const { return EdgePairs (); } + virtual EdgePairs angle_check (double, double, bool) const { return EdgePairs (); } + + virtual RegionDelegate *snapped_in_place (db::Coord, db::Coord) { return this; } + virtual RegionDelegate *snapped (db::Coord, db::Coord) { return new EmptyRegion (); } + + virtual RegionDelegate *strange_polygon_check () const { return new EmptyRegion (); } + + virtual Edges edges (const EdgeFilterBase *) const { return db::Edges (); } + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; } + virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); } + + virtual RegionDelegate *merged_in_place () { return this; } + virtual RegionDelegate *merged_in_place (bool, unsigned int) { return this; } + virtual RegionDelegate *merged () const { return new EmptyRegion (); } + virtual RegionDelegate *merged (bool, unsigned int) const { return new EmptyRegion (); } + + virtual RegionDelegate *sized (coord_type, unsigned int) const { return new EmptyRegion (); } + virtual RegionDelegate *sized (coord_type, coord_type, unsigned int) const { return new EmptyRegion (); } + + virtual RegionDelegate *and_with (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *not_with (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + virtual RegionDelegate *add_in_place (const Region &other); + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_outside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_inside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_inside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_interacting (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_interacting (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_interacting (const Edges &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_interacting (const Edges &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_overlapping (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_overlapping (const Region &) const { return new EmptyRegion (); } + + virtual RegionDelegate *holes () const { return new EmptyRegion (); } + virtual RegionDelegate *hulls () const { return new EmptyRegion (); } + virtual RegionDelegate *in (const Region &, bool) const { return new EmptyRegion (); } + virtual RegionDelegate *rounded_corners (double, double, unsigned int) const { return new EmptyRegion (); } + virtual RegionDelegate *smoothed (coord_type) const { return new EmptyRegion (); } + + virtual bool has_valid_polygons () const { return false; } + virtual const db::Polygon *nth (size_t) const { tl_assert (false); } + + virtual const db::RecursiveShapeIterator *iter () const { return 0; } + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + +private: + EmptyRegion &operator= (const EmptyRegion &other); +}; + +/** + * @brief Provides default flat implementations + */ +class DB_PUBLIC AsIfFlatRegion + : public RegionDelegate +{ +public: + AsIfFlatRegion (); + virtual ~AsIfFlatRegion (); + + virtual bool is_box () const; + + virtual area_type area (const db::Box &box) const; + virtual perimeter_type perimeter (const db::Box &box) const; + virtual Box bbox () const; + + virtual std::string to_string (size_t nmax) const; + + EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const; + virtual EdgePairs angle_check (double min, double max, bool inverse) const; + + virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) + { + return snapped (gx, gy); + } + + virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); + + virtual Edges edges (const EdgeFilterBase *) const; + + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) + { + return filtered (filter); + } + + virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const; + + virtual RegionDelegate *merged_in_place () + { + return merged (); + } + + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc) + { + return merged (min_coherence, min_wc); + } + + virtual RegionDelegate *merged () const + { + return merged (min_coherence (), 0); + } + + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const; + + virtual RegionDelegate *strange_polygon_check () const; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; + + virtual RegionDelegate *and_with (const Region &other) const; + virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + + virtual RegionDelegate *add_in_place (const Region &other) + { + return add (other); + } + + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, false); + } + + virtual RegionDelegate *selected_not_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, true); + } + + virtual RegionDelegate *selected_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, false); + } + + virtual RegionDelegate *selected_not_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, true); + } + + virtual RegionDelegate *selected_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, false); + } + + virtual RegionDelegate *selected_not_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, true); + } + + virtual RegionDelegate *selected_interacting (const Edges &other) const + { + return selected_interacting_generic (other, false); + } + + virtual RegionDelegate *selected_not_interacting (const Edges &other) const + { + return selected_interacting_generic (other, true); + } + + virtual RegionDelegate *selected_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, false); + } + + virtual RegionDelegate *selected_not_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, true); + } + + virtual RegionDelegate *holes () const; + virtual RegionDelegate *hulls () const; + virtual RegionDelegate *in (const Region &other, bool invert) const; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; + virtual RegionDelegate *smoothed (coord_type d) const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + +protected: + void update_bbox (const db::Box &box); + void invalidate_bbox (); + +private: + AsIfFlatRegion &operator= (const AsIfFlatRegion &other); + + mutable bool m_bbox_valid; + mutable db::Box m_bbox; + + void ensure_bbox_valid (); + static RegionDelegate *region_from_box (const db::Box &b); + + EdgePairs run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + EdgePairs run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; + RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const; +}; + +/** + * @brief A flat, polygon-set delegate + */ +class DB_PUBLIC FlatRegion + : public AsIfFlatRegion +{ +public: typedef db::layer polygon_layer_type; - typedef polygon_layer_type::iterator iterator_type; + typedef polygon_layer_type::iterator polygon_iterator_type; - db::RecursiveShapeIterator m_rec_iter; - db::ICplxTrans m_iter_trans; - db::Polygon m_polygon; - iterator_type m_from, m_to; + FlatRegion (); + FlatRegion (const db::Shapes &polygons, bool is_merged); + FlatRegion (bool is_merged); - /** - * @brief ctor from a recursive shape iterator - */ - RegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans), m_from (), m_to () - { - // NOTE: the following initialization appears to be required on some compilers - // (specifically MacOS/clang) to ensure the proper initialization of the iterators - m_from = m_to; - set (); - } + FlatRegion (const FlatRegion &other); - /** - * @brief ctor from a range of polygons inside a vector - */ - RegionIterator (iterator_type from, iterator_type to) - : m_from (from), m_to (to) - { - // no required yet: set (); - } + virtual ~FlatRegion (); - /** - * @brief Establish the iterator at the current position - */ - void set () + RegionDelegate *clone () const { - while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { - inc (); + return new FlatRegion (*this); + } + + void reserve (size_t); + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + virtual bool is_merged () const; + + virtual Box bbox () const; + + virtual RegionDelegate *merged_in_place (); + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); + virtual RegionDelegate *merged () const; + + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter); + + virtual RegionDelegate *add_in_place (const Region &other); + virtual RegionDelegate *add (const Region &other) const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + void insert (const db::Box &box); + void insert (const db::Path &path); + void insert (const db::SimplePolygon &polygon); + void insert (const db::Polygon &polygon); + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon poly; + shape.polygon (poly); + poly.transform (trans); + insert (poly); } - if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().polygon (m_polygon); - m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); - } + } + + template + void insert (const Iter &b, const Iter &e) + { + reserve (size () + (e - b)); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } + + template + void transform (const Trans &trans) + { + if (! trans.is_unity ()) { + for (polygon_iterator_type p = m_polygons.get_layer ().begin (); p != m_polygons.get_layer ().end (); ++p) { + m_polygons.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } + + db::Shapes &raw_polygons (); + +protected: + virtual void merged_semantics_changed (); + void set_box (const db::Box &box); + void invalidate_cache (); + +private: + FlatRegion &operator= (const FlatRegion &other); + + bool m_is_merged; + mutable db::Shapes m_polygons; + mutable db::Shapes m_merged_polygons; + mutable bool m_merged_polygons_valid; + + void init (); + void ensure_merged_polygons_valid () const; +}; + +/** + * @brief An original layerregion based on a RecursiveShapeIterator + */ +class DB_PUBLIC OriginalLayerRegion + : public AsIfFlatRegion +{ +public: + OriginalLayerRegion (); + OriginalLayerRegion (const RecursiveShapeIterator &si); + OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics); + virtual ~OriginalLayerRegion (); + + RegionDelegate *clone () const; + + virtual void enable_progress (const std::string &progress_desc); + virtual void disable_progress (); + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + + virtual bool is_box () const; + virtual bool is_merged () const; + virtual area_type area (const db::Box &box = db::Box ()) const; + virtual perimeter_type perimeter (const db::Box &box = db::Box ()) const; + + virtual Box bbox () const; + + virtual RegionDelegate *merged_in_place (); + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); + virtual RegionDelegate *merged () const; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; + + virtual RegionDelegate *and_with (const Region &other) const; + virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &other) const; + virtual RegionDelegate *selected_not_outside (const Region &other) const; + virtual RegionDelegate *selected_inside (const Region &other) const; + virtual RegionDelegate *selected_not_inside (const Region &other) const; + virtual RegionDelegate *selected_interacting (const Region &other) const; + virtual RegionDelegate *selected_not_interacting (const Region &other) const; + virtual RegionDelegate *selected_interacting (const Edges &other) const; + virtual RegionDelegate *selected_not_interacting (const Edges &other) const; + virtual RegionDelegate *selected_overlapping (const Region &other) const; + virtual RegionDelegate *selected_not_overlapping (const Region &other) const; + + virtual RegionDelegate *holes () const; + virtual RegionDelegate *hulls () const; + virtual RegionDelegate *in (const Region &other, bool invert) const; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; + virtual RegionDelegate *smoothed (coord_type d) const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; +}; + +/** + * @brief A region + * + * A region basically is a set of polygons. It supports a variety of operations, i.e. + * boolean operations with other regions, sizing etc. + * + * Regions can have different states. Specifically a region can be merged (no overlapping + * polygons are present, touching polygons are merged, self-intersections of polygons are + * removed) or non-merged (polygons may overlap or polygons may be self-intersecting). In + * merged state, the wrap count at every point is either zero or 1, in non-merged state + * it can be every value. + * + * Polygons inside the region may contain holes if the region is merged. + */ +class DB_PUBLIC Region + : public gsi::ObjectBase +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::Polygon polygon_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type distance_type; + typedef coord_traits::perimeter_type perimeter_type; + typedef coord_traits::area_type area_type; + typedef RegionIterator const_iterator; + + /** + * @brief Default constructor + * + * Creates an empty region. + */ + Region (); + + /** + * @brief Destructor + */ + ~Region (); + + /** + * @brief Constructor from a delegate + * + * The region will take ownership of the delegate. + */ + Region (RegionDelegate *delegate); + + /** + * @brief Copy constructor + */ + Region (const Region &other); + + /** + * @brief Assignment + */ + Region &operator= (const Region &other); + + /** + * @brief Constructor from an object + * + * Creates a region representing a single instance of that object + */ + template + Region (const Sh &s) + { + FlatRegion *delegate = new FlatRegion (); + mp_delegate = delegate; + delegate->insert (s); } /** - * @brief Increment the iterator + * @brief Sequence constructor + * + * Creates a region from a sequence of objects. The objects can be boxes, + * polygons, paths or shapes. This version accepts iterators of the begin ... end + * style. */ - void inc () + template + Region (const Iter &b, const Iter &e) { - if (! m_rec_iter.at_end ()) { - ++m_rec_iter; - } else { - ++m_from; + FlatRegion *delegate = new FlatRegion (); + mp_delegate = delegate; + delegate->insert (b, e); + } + + /** + * @brief Constructor from a RecursiveShapeIterator + * + * Creates a region from a recursive shape iterator. This allows to feed a region + * from a hierarchy of cells. + */ + Region (const RecursiveShapeIterator &si); + + /** + * @brief Constructor from a RecursiveShapeIterator with a transformation + * + * Creates a region from a recursive shape iterator. This allows to feed a region + * from a hierarchy of cells. The transformation is useful to scale to a specific + * DBU for example. + */ + Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); + + /** + * @brief Gets the underlying delegate object + */ + RegionDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Enable progress reporting + * + * @param progress_text The description text of the progress object + */ + void enable_progress (const std::string &desc = std::string ()) + { + mp_delegate->enable_progress (desc); + } + + /** + * @brief Disable progress reporting + */ + void disable_progress () + { + mp_delegate->disable_progress (); + } + + /** + * @brief Iterator of the region + * + * The iterator delivers the polygons of the region. + * It follows the at_end semantics. + */ + const_iterator begin () const + { + return RegionIterator (mp_delegate->begin ()); + } + + /** + * @brief Returns the merged polygons if merge semantics applies + * + * If merge semantics is not enabled, this iterator delivers the individual polygons. + */ + const_iterator begin_merged () const + { + return RegionIterator (mp_delegate->begin_merged ()); + } + + /** + * @brief Delivers a RecursiveShapeIterator pointing to the polygons plus the necessary transformation + */ + std::pair begin_iter () const + { + return mp_delegate->begin_iter (); + } + + /** + * @brief Delivers a RecursiveShapeIterator pointing to the merged polygons plus the necessary transformation + */ + std::pair begin_merged_iter () const + { + return mp_delegate->begin_merged_iter (); + } + + /** + * @brief Inserts the given shape (working object) into the region + */ + template + void insert (const Sh &shape) + { + flat_region ()->insert (shape); + } + + /** + * @brief Insert a shape reference into the region + */ + void insert (const db::Shape &shape) + { + flat_region ()->insert (shape); + } + + /** + * @brief Insert a transformed shape into the region + */ + template + void insert (const db::Shape &shape, const T &trans) + { + flat_region ()->insert (shape, trans); + } + + /** + * @brief Returns true if the region is empty + */ + bool empty () const + { + return mp_delegate->empty (); + } + + /** + * @brief Returns the number of polygons in the region + */ + size_t size () const + { + return mp_delegate->size (); + } + + /** + * @brief Returns a string representing the region + * + * nmax specifies how many polygons are included (set to std::numeric_limits::max() for "all". + */ + std::string to_string (size_t nmax = 10) const + { + return mp_delegate->to_string (nmax); + } + + /** + * @brief Clear the region + */ + void clear () + { + set_delegate (new EmptyRegion ()); + } + + /** + * @brief Reserve memory for the given number of polygons + */ + void reserve (size_t n) + { + flat_region ()->reserve (n); + } + + /** + * @brief Sets the minimum-coherence flag + * + * If minimum coherence is set, the merge operations (explicit merge with \merge or + * implicit merge through merged_semantics) are performed using minimum coherence mode. + * The coherence mode determines how kissing-corner situations are resolved. If + * minimum coherence is selected, they are resolved such that multiple polygons are + * created which touch at a corner). + */ + void set_min_coherence (bool f) + { + mp_delegate->set_min_coherence (f); + } + + /** + * @brief Gets the minimum coherence flag + */ + bool min_coherence () const + { + return mp_delegate->min_coherence (); + } + + /** + * @brief Sets the merged-semantics flag + * + * If merged semantics is enabled (the default), coherent polygons will be considered + * as single regions and artificial edges such as cut-lines will not be considered. + * Merged semantics thus is equivalent to considering coherent areas rather than + * single polygons. + */ + void set_merged_semantics (bool f) + { + mp_delegate->set_merged_semantics (f); + } + + /** + * @brief Gets the merged-semantics flag + */ + bool merged_semantics () const + { + return mp_delegate->merged_semantics (); + } + + /** + * @brief Enables or disables strict handling + * + * Strict handling means to leave away some optimizations. Specifically the + * output of boolean operations will be merged even if one input is empty. + * Without strict handling, the operation will be optimized and output + * won't be merged. + * + * Strict handling is disabled by default. + */ + void set_strict_handling (bool f) + { + mp_delegate->set_strict_handling (f); + } + + /** + * @brief Gets a valid indicating whether strict handling is enabled + */ + bool strict_handling () const + { + return mp_delegate->strict_handling (); + } + + /** + * @brief Returns true if the region is a single box + * + * If the region is not merged, this method may return false even + * if the merged region would be a box. + */ + bool is_box () const + { + return mp_delegate->is_box (); + } + + /** + * @brief Returns true if the region is merged + */ + bool is_merged () const + { + return mp_delegate->is_merged (); + } + + /** + * @brief Returns the area of the region + * + * This method returns the area sum over all polygons. + * Merged semantics applies. In merged semantics, the area is the correct area covered by the + * polygons. Without merged semantics, overlapping parts are counted twice. + * + * If a box is given, the computation is restricted to that box. + */ + area_type area (const db::Box &box = db::Box ()) const + { + return mp_delegate->area (box); + } + + /** + * @brief Returns the perimeter sum of the region + * + * This method returns the perimeter sum over all polygons. + * Merged semantics applies. In merged semantics, the perimeter is the true perimeter. + * Without merged semantics, inner edges contribute to the perimeter. + * + * If a box is given, the computation is restricted to that box. + * Edges coincident with the box edges are counted only if the form outer edges at the box edge. + */ + perimeter_type perimeter (const db::Box &box = db::Box ()) const + { + return mp_delegate->perimeter (box); + } + + /** + * @brief Returns the bounding box of the region + */ + Box bbox () const + { + return mp_delegate->bbox (); + } + + /** + * @brief Filters the polygons + * + * This method will keep all polygons for which the filter returns true. + * Merged semantics applies. In merged semantics, the filter will run over + * all merged polygons. + */ + Region &filter (const PolygonFilterBase &filter) + { + set_delegate (mp_delegate->filter_in_place (filter)); + return *this; + } + + /** + * @brief Returns the filtered polygons + * + * This method will return a new region with only those polygons which + * conform to the filter criterion. + */ + Region filtered (const PolygonFilterBase &filter) const + { + return Region (mp_delegate->filtered (filter)); + } + + /** + * @brief Applies a width check and returns EdgePairs which correspond to violation markers + * + * The width check will create a edge pairs if the width of the area between the + * edges is less than the specified threshold d. Without "whole_edges", the parts of + * the edges are returned which violate the condition. If "whole_edges" is true, the + * result will contain the complete edges participating in the result. + * + * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" + * metrics are available. + * + * ingore_angle allows specification of a maximum angle that connected edges can have to not participate + * in the check. By choosing 90 degree, edges with angles of 90 degree and larger are not checked, + * but acute corners are for example. + * + * With min_projection and max_projection it is possible to specify how edges must be related + * to each other. If the length of the projection of either edge on the other is >= min_projection + * or < max_projection, the edges are considered for the check. + * + * The order of the edges in the resulting edge pairs is undefined. + * + * Merged semantics applies. + */ + EdgePairs width_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->width_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a space check and returns EdgePairs which correspond to violation markers + * + * For the parameters see \width_check. The space check reports edges for which the space is + * less than the specified threshold d. + * + * Merged semantics applies. + */ + EdgePairs space_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->space_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a isolation check (space of polygon vs. other polygons) and returns EdgePairs which correspond to violation markers + * + * For the parameters see \width_check. The space check reports edges for which the notch space is + * less than the specified threshold d. + * + * Merged semantics applies. + */ + EdgePairs isolated_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a notch check (space in polygon vs. itself) and returns EdgePairs which correspond to violation markers + * + * For the parameters see \width_check. The space check reports edges for which the notch space is + * less than the specified threshold d. + * + * Merged semantics applies. + */ + EdgePairs notch_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->notch_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a enclosed check and returns EdgePairs which correspond to violation markers + * + * The check will return true, where this region is enclosing the polygons of the other + * region by less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->enclosing_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a overlap check and returns EdgePairs which correspond to violation markers + * + * The check will return true, where this region overlaps the polygons of the other + * region by less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->overlap_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a separation check and returns EdgePairs which correspond to violation markers + * + * The check will return true, where this region is separated by polygons of the other + * region by less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->separation_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a inside check and returns EdgePairs which correspond to violation markers + * + * The check will return true, where this region is inside by less than the threshold d inside the polygons of the other + * region. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->inside_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Returns an edge set containing all edges of the polygons in this region + * + * Merged semantics applies. In merged semantics, only full, outer edges are delivered. + */ + Edges edges () const + { + return mp_delegate->edges (0); + } + + /** + * @brief Returns an edge set containing all edges of the polygons in this region + * + * This version allows to specify a filter by which the edges are filtered before they are + * returned. + * + * Merged semantics applies. In merged semantics, only full, outer edges are delivered. + */ + Edges edges (const EdgeFilterBase &filter) const + { + return mp_delegate->edges (&filter); + } + + /** + * @brief Transform the region + */ + template + Region &transform (const T &trans) + { + flat_region ()->transform (trans); + return *this; + } + + /** + * @brief Returns the transformed region + */ + template + Region transformed (const T &trans) const + { + Region d (*this); + d.transform (trans); + return d; + } + + /** + * @brief Performs an off-grid check on the polygons inside the region + * + * The method returns single-point edge pairs for each vertex found off-grid. + * The grid can be specified differently in x and y direction. + */ + EdgePairs grid_check (db::Coord gx, db::Coord gy) const + { + return mp_delegate->grid_check (gx, gy); + } + + /** + * @brief Performs an angle check + * + * The method returns edge pairs for each connected edges having + * an angle between min and max (exclusive max, in degree) or + * not between min and max (if inverse is true). + */ + EdgePairs angle_check (double min, double max, bool inverse) const + { + return mp_delegate->angle_check (min, max, inverse); + } + + /** + * @brief Grid-snaps the region + * + * Snaps the vertices of the polygons to the specified grid. + * different grids can be specified int x and y direction. + */ + void snap (db::Coord gx, db::Coord gy) + { + set_delegate (mp_delegate->snapped_in_place (gx, gy)); + } + + /** + * @brief Returns the snapped region + */ + Region snapped (db::Coord gx, db::Coord gy) const + { + return Region (mp_delegate->snapped (gx, gy)); + } + + /** + * @brief Performs a check for "strange" polygons + * + * This check will return a region with all self-overlapping or + * non-orientable parts of polygons. + * + * Naturally this method will ignore the merged_semantics setting. + */ + Region strange_polygon_check () const + { + return Region (mp_delegate->strange_polygon_check ()); + } + + /** + * @brief Swap with the other region + */ + void swap (db::Region &other) + { + std::swap (other.mp_delegate, mp_delegate); + } + + /** + * @brief Merge the region + * + * This method merges the polygons of the region if they are not merged already. + * It returns a reference to this region. + * An out-of-place merge version is "merged". + */ + Region &merge () + { + set_delegate (mp_delegate->merged_in_place ()); + return *this; + } + + /** + * @brief Returns the merged region + * + * This is the out-of-place merge. It returns a new region but does not modify + * the region it is called on. An in-place version is "merge". + */ + Region merged () const + { + return Region (mp_delegate->merged ()); + } + + /** + * @brief Merge the region with options + * + * This will merge the region and provides some options while doing so. + * + * A result is generated if the wrap count (wc) + * of a point is larger than the given min_wrapcount value. + * A min_wrapcount of 1 will produce output where at least two polygons overlap. + * + * This method will always execute the merge, even if the region is already merged. + * + * @param min_coherence Set this parameter to true to get minimum polygons (kissing corner problem) + * @param min_wrapcount See the description above + * @return A reference to this region + */ + Region &merge (bool min_coherence, unsigned int min_wc = 0) + { + set_delegate (mp_delegate->merged_in_place (min_coherence, min_wc)); + return *this; + } + + /** + * @brief Returns the merged region with options + * + * This is the out-of-place version of "merge" with options (see there). + */ + Region merged (bool min_coherence, unsigned int min_wc = 0) const + { + return Region (mp_delegate->merged (min_coherence, min_wc)); + } + + /** + * @brief Size the region + * + * This method applies a sizing to the region. Before the sizing is done, the + * region is merged if this is not the case already. + * + * The result is a set of polygons which may be overlapping, but are not self- + * intersecting. + * + * Merged semantics applies. + * + * @param d The (isotropic) sizing value + * @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance. + * @return A reference to self + */ + Region &size (coord_type d, unsigned int mode = 2) + { + set_delegate (mp_delegate->sized (d, mode)); + return *this; + } + + /** + * @brief Anisotropic sizing + * + * This version provides anisotropic sizing by allowing to specify a distance int x and y + * direction. + * + * Merged semantics applies. + * + * @param dx The horizontal sizing + * @param dy The vertical sizing + * @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance. + */ + Region &size (coord_type dx, coord_type dy, unsigned int mode = 2) + { + set_delegate (mp_delegate->sized (dx, dy, mode)); + return *this; + } + + /** + * @brief Returns the sized region + * + * This is an out-of-place version of the size method with isotropic sizing + * "merged polygon" semantics applies if merged_polygon_semantics is true (see set_auto_merge). + * + * Merged semantics applies. + */ + Region sized (coord_type d, unsigned int mode = 2) const + { + return Region (mp_delegate->sized (d, mode)); + } + + /** + * @brief Returns the sized region + * + * This is an out-of-place version of the size method with anisotropic sizing + * "merged polygon" semantics applies if merged_polygon_semantics is true (see set_auto_merge). + * + * Merged semantics applies. + */ + Region sized (coord_type dx, coord_type dy, unsigned int mode = 2) const + { + return Region (mp_delegate->sized (dx, dy, mode)); + } + + /** + * @brief Boolean AND operator + */ + Region operator& (const Region &other) const + { + return Region (mp_delegate->and_with (other)); + } + + /** + * @brief In-place boolean AND operator + * + * This method does not necessarily merge the region. To ensure the region + * is merged, call merge afterwards. + */ + Region &operator&= (const Region &other) + { + set_delegate (mp_delegate->and_with (other)); + return *this; + } + + /** + * @brief Boolean NOT operator + */ + Region operator- (const Region &other) const + { + return Region (mp_delegate->not_with (other)); + } + + /** + * @brief In-place boolean NOT operator + * + * This method does not necessarily merge the region. To ensure the region + * is merged, call merge afterwards. + */ + Region &operator-= (const Region &other) + { + set_delegate (mp_delegate->not_with (other)); + return *this; + } + + /** + * @brief Boolean XOR operator + */ + Region operator^ (const Region &other) const + { + return Region (mp_delegate->xor_with (other)); + } + + /** + * @brief In-place boolean XOR operator + * + * This method does not necessarily merge the region. To ensure the region + * is merged, call merge afterwards. + */ + Region &operator^= (const Region &other) + { + set_delegate (mp_delegate->xor_with (other)); + return *this; + } + + /** + * @brief Boolean OR operator + * + * This method merges the polygons of both regions. + */ + Region operator| (const Region &other) const + { + return Region (mp_delegate->or_with (other)); + } + + /** + * @brief In-place boolean OR operator + */ + Region &operator|= (const Region &other) + { + set_delegate (mp_delegate->or_with (other)); + return *this; + } + + /** + * @brief Joining of regions + * + * This method joins the regions but does not merge them afterwards. + */ + Region operator+ (const Region &other) const + { + return Region (mp_delegate->add (other)); + } + + /** + * @brief In-place region joining + */ + Region &operator+= (const Region &other) + { + set_delegate (mp_delegate->add_in_place (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which are completly outside polygons from the other region + * + * Merged semantics applies. + */ + Region &select_outside (const Region &other) + { + set_delegate (mp_delegate->selected_outside (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which are not completly outside polygons from the other region + * + * Merged semantics applies. + */ + Region &select_not_outside (const Region &other) + { + set_delegate (mp_delegate->selected_not_outside (other)); + return *this; + } + + /** + * @brief Returns all polygons of this which are completly outside polygons from the other region + * + * This method is an out-of-place version of select_outside. + * + * Merged semantics applies. + */ + Region selected_outside (const Region &other) const + { + return Region (mp_delegate->selected_outside (other)); + } + + /** + * @brief Returns all polygons of this which are not completly outside polygons from the other region + * + * This method is an out-of-place version of select_not_outside. + * + * Merged semantics applies. + */ + Region selected_not_outside (const Region &other) const + { + return Region (mp_delegate->selected_not_outside (other)); + } + + /** + * @brief Selects all polygons of this region which are completly inside polygons from the other region + * + * Merged semantics applies. + */ + Region &select_inside (const Region &other) + { + set_delegate (mp_delegate->selected_inside (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which are not completly inside polygons from the other region + * + * Merged semantics applies. + */ + Region &select_not_inside (const Region &other) + { + set_delegate (mp_delegate->selected_not_inside (other)); + return *this; + } + + /** + * @brief Returns all polygons of this which are completly inside polygons from the other region + * + * This method is an out-of-place version of select_inside. + * + * Merged semantics applies. + */ + Region selected_inside (const Region &other) const + { + return Region (mp_delegate->selected_inside (other)); + } + + /** + * @brief Returns all polygons of this which are not completly inside polygons from the other region + * + * This method is an out-of-place version of select_not_inside. + * + * Merged semantics applies. + */ + Region selected_not_inside (const Region &other) const + { + return Region (mp_delegate->selected_not_inside (other)); + } + + /** + * @brief Selects all polygons of this region which overlap or touch polygons from the other region + * + * Merged semantics applies. + */ + Region &select_interacting (const Region &other) + { + set_delegate (mp_delegate->selected_interacting (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which do not overlap or touch polygons from the other region + * + * Merged semantics applies. + */ + Region &select_not_interacting (const Region &other) + { + set_delegate (mp_delegate->selected_not_interacting (other)); + return *this; + } + + /** + * @brief Returns all polygons of this which overlap or touch polygons from the other region + * + * This method is an out-of-place version of select_interacting. + * + * Merged semantics applies. + */ + Region selected_interacting (const Region &other) const + { + return Region (mp_delegate->selected_interacting (other)); + } + + /** + * @brief Returns all polygons of this which do not overlap or touch polygons from the other region + * + * This method is an out-of-place version of select_not_interacting. + * + * Merged semantics applies. + */ + Region selected_not_interacting (const Region &other) const + { + return Region (mp_delegate->selected_not_interacting (other)); + } + + /** + * @brief Selects all polygons of this region which overlap or touch edges from the given edge collection + * + * Merged semantics applies to both operators. + */ + Region &select_interacting (const Edges &other) + { + set_delegate (mp_delegate->selected_interacting (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which do not overlap or touch edges from the edge collection + * + * Merged semantics applies to both operators. + */ + Region &select_not_interacting (const Edges &other) + { + set_delegate (mp_delegate->selected_not_interacting (other)); + return *this; + } + + /** + * @brief Returns all polygons of this which overlap or touch edges from the edge collection + * + * This method is an out-of-place version of select_interacting. + * + * Merged semantics applies to both operators. + */ + Region selected_interacting (const Edges &other) const + { + return Region (mp_delegate->selected_interacting (other)); + } + + /** + * @brief Returns all polygons of this which do not overlap or touch polygons from the other region + * + * This method is an out-of-place version of select_not_interacting. + * + * Merged semantics applies to both operators. + */ + Region selected_not_interacting (const Edges &other) const + { + return Region (mp_delegate->selected_not_interacting (other)); + } + + /** + * @brief Selects all polygons of this region which overlap polygons from the other region + * + * Merged semantics applies. + */ + Region &select_overlapping (const Region &other) + { + set_delegate (mp_delegate->selected_overlapping (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which do not overlap polygons from the other region + * + * Merged semantics applies. + */ + Region &select_not_overlapping (const Region &other) + { + set_delegate (mp_delegate->selected_not_overlapping (other)); + return *this; + } + + /** + * @brief Returns all polygons of this which overlap polygons from the other region + * + * This method is an out-of-place version of select_overlapping. + * + * Merged semantics applies. + */ + Region selected_overlapping (const Region &other) const + { + return Region (mp_delegate->selected_overlapping (other)); + } + + /** + * @brief Returns all polygons of this which do not overlap polygons from the other region + * + * This method is an out-of-place version of select_not_overlapping. + * + * Merged semantics applies. + */ + Region selected_not_overlapping (const Region &other) const + { + return Region (mp_delegate->selected_not_overlapping (other)); + } + + /** + * @brief Returns the holes + * + * This method returns the holes of the polygons. + * + * Merged semantics applies. + */ + Region holes () const + { + return Region (mp_delegate->holes ()); + } + + /** + * @brief Returns the hulls + * + * This method returns the hulls of the polygons. It does not merge the + * polygons before the hulls are derived. + * + * Merged semantics applies. + */ + Region hulls () const + { + return Region (mp_delegate->hulls ()); + } + + /** + * @brief Returns all polygons which are in the other region + * + * This method will return all polygons which are part of another region. + * The match is done exactly. + * The "invert" flag can be used to invert the sense, i.e. with + * "invert" set to true, this method will return all polygons not + * in the other region. + * + * Merged semantics applies. + */ + Region in (const Region &other, bool invert = false) const + { + return Region (mp_delegate->in (other, invert)); + } + + /** + * @brief Round corners (in-place) + * + * @param rinner The inner radius in DBU units + * @param router The outer radius in DBU units + * @param n The number of points to use per circle + */ + void round_corners (double rinner, double router, unsigned int n) + { + set_delegate (mp_delegate->rounded_corners (rinner, router, n)); + } + + /** + * @brief Returns a new region with rounded corners (out of place) + */ + Region rounded_corners (double rinner, double router, unsigned int n) const + { + return Region (mp_delegate->rounded_corners (rinner, router, n)); + } + + /** + * @brief Smoothes the region (in-place) + */ + void smooth (coord_type d) + { + set_delegate (mp_delegate->smoothed (d)); + } + + /** + * @brief Returns the smoothed region + * + * @param d The smoothing accuracy + */ + Region smoothed (coord_type d) const + { + return Region (mp_delegate->smoothed (d)); + } + + /** + * @brief Returns the nth polygon + * + * This operation is only cheap if "has_valid_polygons" is true. Otherwise, the + * complexity is O(n). + */ + const db::Polygon *nth (size_t n) const + { + return mp_delegate->nth (n); + } + + /** + * @brief Returns true, if the region has valid polygons stored within itself + */ + bool has_valid_polygons () const + { + return mp_delegate->has_valid_polygons (); + } + + /** + * @brief Gets the internal iterator + * + * This method is intended for users who know what they are doing + */ + const db::RecursiveShapeIterator &iter () const; + + /** + * @brief Equality + */ + bool operator== (const db::Region &other) const + { + return mp_delegate->equals (other); + } + + /** + * @brief Inequality + */ + bool operator!= (const db::Region &other) const + { + return ! mp_delegate->equals (other); + } + + /** + * @brief Less operator + */ + bool operator< (const db::Region &other) const + { + return mp_delegate->less (other); + } + +private: + RegionDelegate *mp_delegate; + + void set_delegate (RegionDelegate *delegate) + { + if (delegate != mp_delegate) { + delete mp_delegate; + mp_delegate = delegate; } } + + FlatRegion *flat_region () + { + FlatRegion *region = dynamic_cast (mp_delegate); + if (! region) { + region = new FlatRegion (); + region->insert_seq (begin ()); + set_delegate (region); + } + + return region; + } }; + +// ...................................................................................................... + +#if 0 // ORIGINAL /** * @brief A region * @@ -400,7 +2262,6 @@ private: * * Polygons inside the region may contain holes if the region is merged. */ - class DB_PUBLIC Region : public gsi::ObjectBase { @@ -1632,6 +3493,7 @@ private: Region *mp_region; bool m_clear; }; +#endif } // namespace db From 5a5051b0dd202efe12e6294dd43ff649285a9f60 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 Nov 2018 23:26:08 +0100 Subject: [PATCH 034/335] WIP: second part of region refactoring. --- src/db/db/dbRegion.cc | 376 ++++++++++++++++++++++-------------------- src/db/db/dbRegion.h | 57 ++----- 2 files changed, 214 insertions(+), 219 deletions(-) diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 4a2761b77..b8f234dba 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -261,6 +261,16 @@ AsIfFlatRegion::in (const Region &other, bool invert) const return new_region.release (); } +size_t +AsIfFlatRegion::size () const +{ + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + ++n; + } + return n; +} + bool AsIfFlatRegion::is_box () const { @@ -1971,217 +1981,229 @@ FlatRegion::insert (const db::Shape &shape) } // ------------------------------------------------------------------------------------------------------------- +// OriginalLayerRegion implementation -#if 0 // @@@ ORIGINAL -/** - * @brief A region iterator - * - * The iterator delivers the polygons of the region - */ - -class DB_PUBLIC RegionIterator +namespace { -public: - typedef db::Polygon value_type; - typedef const db::Polygon &reference; - typedef const db::Polygon *pointer; - typedef std::forward_iterator_tag iterator_category; - typedef void difference_type; - /** - * @Returns true, if the iterator is at the end - */ - bool at_end () const + class OriginalLayerRegionIterator + : public RegionIteratorDelegate { - return m_from == m_to && m_rec_iter.at_end (); - } - - /** - * @brief Increment - */ - RegionIterator &operator++ () - { - inc (); - set (); - return *this; - } - - /** - * @brief Access - */ - reference operator* () const - { - if (m_rec_iter.at_end ()) { - return *m_from; - } else { - return m_polygon; + public: + OriginalLayerRegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) + : m_rec_iter (iter), m_iter_trans (trans) + { + set (); } - } - /** - * @brief Access - */ - pointer operator-> () const - { - if (m_rec_iter.at_end ()) { - return &*m_from; - } else { + virtual bool at_end () const + { + return m_rec_iter.at_end (); + } + + virtual void increment () + { + inc (); + set (); + } + + virtual const value_type *get () const + { return &m_polygon; } - } -private: - friend class Region; - - typedef db::layer polygon_layer_type; - typedef polygon_layer_type::iterator iterator_type; - - db::RecursiveShapeIterator m_rec_iter; - db::ICplxTrans m_iter_trans; - db::Polygon m_polygon; - iterator_type m_from, m_to; - - /** - * @brief ctor from a recursive shape iterator - */ - RegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans), m_from (), m_to () - { - // NOTE: the following initialization appears to be required on some compilers - // (specifically MacOS/clang) to ensure the proper initialization of the iterators - m_from = m_to; - set (); - } - - /** - * @brief ctor from a range of polygons inside a vector - */ - RegionIterator (iterator_type from, iterator_type to) - : m_from (from), m_to (to) - { - // no required yet: set (); - } - - /** - * @brief Establish the iterator at the current position - */ - void set () - { - while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { - inc (); + virtual RegionIteratorDelegate *clone () const + { + return new OriginalLayerRegionIterator (*this); } - if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().polygon (m_polygon); - m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); + + private: + friend class Region; + + db::RecursiveShapeIterator m_rec_iter; + db::ICplxTrans m_iter_trans; + db::Polygon m_polygon; + + void set () + { + while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { + ++m_rec_iter; + } + if (! m_rec_iter.at_end ()) { + m_rec_iter.shape ().polygon (m_polygon); + m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); + } } - } - /** - * @brief Increment the iterator - */ - void inc () - { - if (! m_rec_iter.at_end ()) { - ++m_rec_iter; - } else { - ++m_from; + void inc () + { + if (! m_rec_iter.at_end ()) { + ++m_rec_iter; + } } - } -}; -#endif + }; -#if 0 // @@@ TODO -// .......................................................... +} - - -/** - * @brief An original layerregion based on a RecursiveShapeIterator - */ -class DB_PUBLIC OriginalLayerRegion - : public RegionDelegate +OriginalLayerRegion::OriginalLayerRegion () + : AsIfFlatRegion () { -public: - OriginalLayerRegion () { } - virtual ~OriginalLayerRegion () { } + init (); +} - RegionDelegate *clone () const; +OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged) + : AsIfFlatRegion (), m_iter (si) +{ + init (); - virtual void enable_progress (const std::string &progress_desc); - virtual void disable_progress (); + m_is_merged = is_merged; +} - virtual RegionIteratorDelegate *begin () const; - virtual RegionIteratorDelegate *begin_merged () const; +OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) + : AsIfFlatRegion (), m_iter (si), m_iter_trans (trans) +{ + init (); - virtual std::pair begin_iter () const; - virtual std::pair begin_merged_iter () const; + m_is_merged = is_merged; + set_merged_semantics (merged_semantics); +} - virtual bool empty () const; - virtual size_t size () const; +OriginalLayerRegion::~OriginalLayerRegion () +{ + // .. nothing yet .. +} - virtual bool is_box () const; - virtual bool is_merged () const; - virtual area_type area (const db::Box &box = db::Box ()) const; - virtual perimeter_type perimeter (const db::Box &box = db::Box ()) const; +RegionDelegate * +OriginalLayerRegion::clone () const +{ + return new OriginalLayerRegion (*this); +} - virtual Box bbox () const; +RegionIteratorDelegate * +OriginalLayerRegion::begin () const +{ + return new OriginalLayerRegionIterator (m_iter, m_iter_trans); +} - virtual EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - virtual EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - virtual EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - virtual EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - virtual EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - virtual EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - virtual EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - virtual EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const; - virtual EdgePairs angle_check (double min, double max, bool inverse) const; - virtual void snap (db::Coord gx, db::Coord gy); - virtual Region strange_polygon_check () const; +RegionIteratorDelegate * +OriginalLayerRegion::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_polygons_valid (); + return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); + } +} - virtual Edges edges () const; +std::pair +OriginalLayerRegion::begin_iter () const +{ + return std::make_pair (m_iter, m_iter_trans); +} - virtual void transform (const db::ICplxTrans &trans); - virtual void transform (const db::Trans &trans); - virtual void merge (); - virtual void merge (bool min_coherence, unsigned int min_wc); +std::pair +OriginalLayerRegion::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_polygons_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); + } +} - virtual RegionDelegate *sized (coord_type d, unsigned int mode); - virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode); +bool +OriginalLayerRegion::empty () const +{ + return m_iter.at_end (); +} - virtual RegionDelegate *and_with (const Region &other) const; - virtual RegionDelegate *not_with (const Region &other) const; - virtual RegionDelegate *xor_with (const Region &other) const; - virtual RegionDelegate *or_with (const Region &other) const; - virtual RegionDelegate *add (const Region &other) const; +bool +OriginalLayerRegion::is_merged () const +{ + return m_is_merged; +} - virtual RegionDelegate *selected_outside (const Region &other) const; - virtual RegionDelegate *selected_not_outside (const Region &other) const; - virtual RegionDelegate *selected_inside (const Region &other) const; - virtual RegionDelegate *selected_not_inside (const Region &other) const; - virtual RegionDelegate *selected_interacting (const Region &other) const; - virtual RegionDelegate *selected_not_interacting (const Region &other) const; - virtual RegionDelegate *selected_interacting (const Edges &other) const; - virtual RegionDelegate *selected_not_interacting (const Edges &other) const; - virtual RegionDelegate *selected_overlapping (const Region &other) const; - virtual RegionDelegate *selected_not_overlapping (const Region &other) const; +const db::Polygon * +OriginalLayerRegion::nth (size_t) const +{ + tl_assert (false); +} - virtual RegionDelegate *holes () const; - virtual RegionDelegate *hulls () const; - virtual RegionDelegate *in (const Region &other, bool invert) const; - virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; - virtual RegionDelegate *smoothed (coord_type d) const; +bool +OriginalLayerRegion::has_valid_polygons () const +{ + return false; +} - virtual const db::Polygon *nth (size_t n) const; - virtual bool has_valid_polygons () const; +const db::RecursiveShapeIterator * +OriginalLayerRegion::iter () const +{ + return &m_iter; +} - virtual const db::RecursiveShapeIterator *iter () const; +bool +OriginalLayerRegion::equals (const Region &other) const +{ + const OriginalLayerRegion *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return true; + } else { + return AsIfFlatRegion::equals (other); + } +} - virtual bool equals (const Region &other) const; - virtual bool less (const Region &other) const; -}; +bool +OriginalLayerRegion::less (const Region &other) const +{ + const OriginalLayerRegion *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return false; + } else { + return AsIfFlatRegion::less (other); + } +} -#endif +void +OriginalLayerRegion::init () +{ + m_is_merged = true; + m_merged_polygons_valid = false; +} + +void +OriginalLayerRegion::ensure_merged_polygons_valid () const +{ + if (! m_merged_polygons_valid) { + + m_merged_polygons.clear (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (0); + db::ShapeGenerator pc (m_merged_polygons); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + m_merged_polygons_valid = true; + + } +} // ------------------------------------------------------------------------------------------------------------- // Region implementation diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 9346cb7aa..bd357b6b7 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -655,6 +655,7 @@ public: virtual ~AsIfFlatRegion (); virtual bool is_box () const; + virtual size_t size () const; virtual area_type area (const db::Box &box) const; virtual perimeter_type perimeter (const db::Box &box) const; @@ -956,15 +957,12 @@ class DB_PUBLIC OriginalLayerRegion { public: OriginalLayerRegion (); - OriginalLayerRegion (const RecursiveShapeIterator &si); - OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics); + OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged = false); + OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); virtual ~OriginalLayerRegion (); RegionDelegate *clone () const; - virtual void enable_progress (const std::string &progress_desc); - virtual void disable_progress (); - virtual RegionIteratorDelegate *begin () const; virtual RegionIteratorDelegate *begin_merged () const; @@ -972,45 +970,8 @@ public: virtual std::pair begin_merged_iter () const; virtual bool empty () const; - virtual size_t size () const; - virtual bool is_box () const; virtual bool is_merged () const; - virtual area_type area (const db::Box &box = db::Box ()) const; - virtual perimeter_type perimeter (const db::Box &box = db::Box ()) const; - - virtual Box bbox () const; - - virtual RegionDelegate *merged_in_place (); - virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); - virtual RegionDelegate *merged () const; - virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const; - - virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; - virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; - - virtual RegionDelegate *and_with (const Region &other) const; - virtual RegionDelegate *not_with (const Region &other) const; - virtual RegionDelegate *xor_with (const Region &other) const; - virtual RegionDelegate *or_with (const Region &other) const; - virtual RegionDelegate *add (const Region &other) const; - - virtual RegionDelegate *selected_outside (const Region &other) const; - virtual RegionDelegate *selected_not_outside (const Region &other) const; - virtual RegionDelegate *selected_inside (const Region &other) const; - virtual RegionDelegate *selected_not_inside (const Region &other) const; - virtual RegionDelegate *selected_interacting (const Region &other) const; - virtual RegionDelegate *selected_not_interacting (const Region &other) const; - virtual RegionDelegate *selected_interacting (const Edges &other) const; - virtual RegionDelegate *selected_not_interacting (const Edges &other) const; - virtual RegionDelegate *selected_overlapping (const Region &other) const; - virtual RegionDelegate *selected_not_overlapping (const Region &other) const; - - virtual RegionDelegate *holes () const; - virtual RegionDelegate *hulls () const; - virtual RegionDelegate *in (const Region &other, bool invert) const; - virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; - virtual RegionDelegate *smoothed (coord_type d) const; virtual const db::Polygon *nth (size_t n) const; virtual bool has_valid_polygons () const; @@ -1019,6 +980,18 @@ public: virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; + +private: + FlatRegion &operator= (const FlatRegion &other); + + bool m_is_merged; + mutable db::Shapes m_merged_polygons; + mutable bool m_merged_polygons_valid; + mutable db::RecursiveShapeIterator m_iter; + db::ICplxTrans m_iter_trans; + + void init (); + void ensure_merged_polygons_valid () const; }; /** From 7062c4acf55af7df3dc0a2b31fa869336e93e6d7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 5 Nov 2018 00:25:16 +0100 Subject: [PATCH 035/335] WIP: next part of region refactoring. --- src/db/db/dbEdges.cc | 2 -- src/db/db/dbEdges.h | 10 ++++------ src/db/db/dbRegion.h | 2 +- src/db/unit_tests/dbTilingProcessor.cc | 1 - 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 5c41840c6..9d2fa8d39 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -724,7 +724,6 @@ Edges::select_interacting (const Region &other) scanner.insert ((char *) &*e, 0); } - other.ensure_valid_polygons (); for (Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { scanner.insert ((char *) &*p + 1, 1); } @@ -754,7 +753,6 @@ Edges::select_not_interacting (const Region &other) scanner.insert ((char *) &*e, 0); } - other.ensure_valid_polygons (); for (Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { scanner.insert ((char *) &*p + 1, 1); } diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index fae01830e..3f31351dc 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -581,12 +581,11 @@ public: * This method will keep all edges for which the filter returns true. * Merged semantics applies. */ - template - Edges &filter (F &filter) + Edges &filter (EdgeFilterBase &filter) { edge_iterator_type ew = m_edges.get_layer ().begin (); for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - if (filter (*e)) { + if (filter.selected (*e)) { if (ew == m_edges.get_layer ().end ()) { m_edges.get_layer ().insert (*e); ew = m_edges.get_layer ().end (); @@ -609,12 +608,11 @@ public: * conform to the filter criterion. * Merged semantics applies. */ - template - Edges filtered (F &filter) const + Edges filtered (const EdgeFilterBase &filter) const { Edges d; for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - if (filter (*e)) { + if (filter.selected (*e)) { d.insert (*e); } } diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index bd357b6b7..e640125b5 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -3422,6 +3422,7 @@ private: Region selected_interacting_generic (const Edges &other, bool inverse) const; void select_interacting_generic (const Edges &other, bool inverse); }; +#endif /** * @brief A polygon receiver putting the polygons into a Region object @@ -3466,7 +3467,6 @@ private: Region *mp_region; bool m_clear; }; -#endif } // namespace db diff --git a/src/db/unit_tests/dbTilingProcessor.cc b/src/db/unit_tests/dbTilingProcessor.cc index cb67d5aad..5aa0b1216 100644 --- a/src/db/unit_tests/dbTilingProcessor.cc +++ b/src/db/unit_tests/dbTilingProcessor.cc @@ -237,7 +237,6 @@ TEST(3) tp.input ("i2", ir2.begin_iter ().first, ir2.begin_iter ().second); EXPECT_EQ (ir2.has_valid_polygons (), false); db::Region ir3 (db::RecursiveShapeIterator (ly, ly.cell (top), l3)); - ir3.ensure_valid_polygons (); tp.input ("i3", ir3.begin_iter ().first, ir3.begin_iter ().second); EXPECT_EQ (ir3.has_valid_polygons (), true); tp.output ("o1", ly, top, o1); From f604d149b53c4738b58071fb82feb36ca6635ac8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 5 Nov 2018 00:32:04 +0100 Subject: [PATCH 036/335] WIP: next part of region refactoring. --- src/db/db/dbRegion.cc | 12 ++++++++++++ src/db/db/dbRegion.h | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index b8f234dba..89a48b81f 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -133,6 +133,18 @@ EmptyRegion::add (const Region &other) const return other.delegate ()->clone (); } +bool +EmptyRegion::equals (const Region &other) const +{ + return other.empty (); +} + +bool +EmptyRegion::less (const Region &other) const +{ + return other.empty () ? false : true; +} + // ------------------------------------------------------------------------------------------------------------- // AsIfFlagRegion implementation diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index e640125b5..e66c1f34e 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -584,8 +584,8 @@ public: virtual EdgePairs space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } virtual EdgePairs isolated_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } virtual EdgePairs notch_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } - virtual EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - virtual EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs enclosing_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs overlap_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } virtual EdgePairs separation_check (const Region &, db::Coord, bool , metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } virtual EdgePairs inside_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } virtual EdgePairs grid_check (db::Coord, db::Coord) const { return EdgePairs (); } From e595c32fe1e3b9e7438249903f1ca9ab95197089 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 6 Nov 2018 00:51:39 +0100 Subject: [PATCH 037/335] WIP: made first unit tests functional again. --- src/db/db/dbRegion.cc | 53 +++++++++++++++++++++++++++---------------- src/db/db/dbRegion.h | 9 ++++---- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 89a48b81f..4ab3ab950 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -369,22 +369,32 @@ AsIfFlatRegion::perimeter (const db::Box &box) const Box AsIfFlatRegion::bbox () const { if (! m_bbox_valid) { - m_bbox = db::Box (); - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - m_bbox += p->box (); - } + m_bbox = compute_bbox (); m_bbox_valid = true; } - return m_bbox; } +Box AsIfFlatRegion::compute_bbox () const +{ + db::Box b; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + b += p->box (); + } + return b; +} + void AsIfFlatRegion::update_bbox (const db::Box &b) { m_bbox = b; m_bbox_valid = true; } +void AsIfFlatRegion::invalidate_bbox () +{ + m_bbox_valid = false; +} + RegionDelegate * AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const { @@ -1612,7 +1622,7 @@ namespace FlatRegion::FlatRegion () - : AsIfFlatRegion () + : AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false) { init (); } @@ -1751,18 +1761,10 @@ bool FlatRegion::is_merged () const return m_is_merged; } -void FlatRegion::set_box (const db::Box &b) +Box FlatRegion::compute_bbox () const { - m_polygons.clear (); - if (! b.empty () && b.width () > 0 && b.height () > 0 ) { - m_polygons.insert (db::Polygon (b)); - } - - m_is_merged = true; - update_bbox (b); - - m_merged_polygons.clear (); - m_merged_polygons_valid = false; + m_polygons.update_bbox (); + return m_polygons.bbox (); } RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter) @@ -2058,13 +2060,24 @@ namespace } OriginalLayerRegion::OriginalLayerRegion () - : AsIfFlatRegion () + : AsIfFlatRegion (), m_merged_polygons (false) { init (); } +OriginalLayerRegion::OriginalLayerRegion (const OriginalLayerRegion &other) + : AsIfFlatRegion (other), + m_is_merged (other.m_is_merged), + m_merged_polygons (other.m_merged_polygons), + m_merged_polygons_valid (other.m_merged_polygons_valid), + m_iter (other.m_iter), + m_iter_trans (other.m_iter_trans) +{ + // .. nothing yet .. +} + OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged) - : AsIfFlatRegion (), m_iter (si) + : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si) { init (); @@ -2072,7 +2085,7 @@ OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, bool } OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) - : AsIfFlatRegion (), m_iter (si), m_iter_trans (trans) + : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si), m_iter_trans (trans) { init (); diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index e66c1f34e..269532762 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -825,7 +825,7 @@ private: mutable bool m_bbox_valid; mutable db::Box m_bbox; - void ensure_bbox_valid (); + virtual db::Box compute_bbox () const; static RegionDelegate *region_from_box (const db::Box &b); EdgePairs run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; @@ -869,8 +869,6 @@ public: virtual size_t size () const; virtual bool is_merged () const; - virtual Box bbox () const; - virtual RegionDelegate *merged_in_place (); virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); virtual RegionDelegate *merged () const; @@ -930,11 +928,11 @@ public: } } - db::Shapes &raw_polygons (); + db::Shapes &raw_polygons () { return m_polygons; } protected: virtual void merged_semantics_changed (); - void set_box (const db::Box &box); + virtual Box compute_bbox () const; void invalidate_cache (); private: @@ -957,6 +955,7 @@ class DB_PUBLIC OriginalLayerRegion { public: OriginalLayerRegion (); + OriginalLayerRegion (const OriginalLayerRegion &other); OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged = false); OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); virtual ~OriginalLayerRegion (); From 9c92a9c72e94bddc3e83b53731ef15dc971fea3b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 7 Nov 2018 00:45:29 +0100 Subject: [PATCH 038/335] Region refactoring: many unit tests are passing again. --- src/db/db/dbRegion.cc | 104 +++++++++++++++++++++++++++------- src/db/db/dbRegion.h | 33 ++++------- src/db/unit_tests/dbRegion.cc | 11 +++- 3 files changed, 106 insertions(+), 42 deletions(-) diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 4ab3ab950..c63ae42a5 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -50,6 +50,23 @@ RegionDelegate::RegionDelegate () m_merge_min_coherence = false; } +RegionDelegate::RegionDelegate (const RegionDelegate &other) +{ + operator= (other); +} + +RegionDelegate & +RegionDelegate::operator= (const RegionDelegate &other) +{ + if (this != &other) { + m_report_progress = other.m_report_progress; + m_merged_semantics = other.m_merged_semantics; + m_strict_handling = other.m_strict_handling; + m_merge_min_coherence = other.m_merge_min_coherence; + } + return *this; +} + RegionDelegate::~RegionDelegate () { // .. nothing yet .. @@ -109,18 +126,6 @@ EmptyRegion::clone () const return new EmptyRegion (*this); } -RegionDelegate * -EmptyRegion::xor_with (const Region &other) const -{ - return other.delegate ()->clone (); -} - -RegionDelegate * -EmptyRegion::or_with (const Region &other) const -{ - return other.delegate ()->clone (); -} - RegionDelegate * EmptyRegion::add_in_place (const Region &other) { @@ -133,6 +138,24 @@ EmptyRegion::add (const Region &other) const return other.delegate ()->clone (); } +RegionDelegate * +EmptyRegion::xor_with (const Region &other) const +{ + return or_with (other); +} + +RegionDelegate * +EmptyRegion::or_with (const Region &other) const +{ + if (other.empty ()) { + return new EmptyRegion (); + } else if (! other.strict_handling ()) { + return other.delegate ()->clone (); + } else { + return other.delegate ()->merged (); + } +} + bool EmptyRegion::equals (const Region &other) const { @@ -1267,7 +1290,7 @@ AsIfFlatRegion::and_with (const Region &other) const // map AND with box to clip .. db::Box b = bbox (); - std::auto_ptr new_region (new FlatRegion ()); + std::auto_ptr new_region (new FlatRegion (false)); std::vector clipped; for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { @@ -1282,7 +1305,7 @@ AsIfFlatRegion::and_with (const Region &other) const // map AND with box to clip .. db::Box b = other.bbox (); - std::auto_ptr new_region (new FlatRegion ()); + std::auto_ptr new_region (new FlatRegion (false)); std::vector clipped; for (RegionIterator p (begin ()); ! p.at_end (); ++p) { @@ -1500,6 +1523,8 @@ AsIfFlatRegion::add (const Region &other) const if (other_flat) { std::auto_ptr new_region (new FlatRegion (*other_flat)); + new_region->set_is_merged (false); + new_region->invalidate_cache (); size_t n = new_region->raw_polygons ().size () + size (); @@ -1659,6 +1684,11 @@ FlatRegion::FlatRegion (bool is_merged) m_is_merged = is_merged; } +void FlatRegion::set_is_merged (bool m) +{ + m_is_merged = m; +} + void FlatRegion::invalidate_cache () { invalidate_bbox (); @@ -1872,6 +1902,8 @@ RegionDelegate *FlatRegion::merged () const RegionDelegate *FlatRegion::add (const Region &other) const { std::auto_ptr new_region (new FlatRegion (*this)); + new_region->invalidate_cache (); + new_region->set_is_merged (false); FlatRegion *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { @@ -1899,6 +1931,7 @@ RegionDelegate *FlatRegion::add (const Region &other) const RegionDelegate *FlatRegion::add_in_place (const Region &other) { invalidate_cache (); + m_is_merged = false; FlatRegion *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { @@ -1920,8 +1953,6 @@ RegionDelegate *FlatRegion::add_in_place (const Region &other) } - m_is_merged = false; - return this; } @@ -1944,9 +1975,21 @@ void FlatRegion::insert (const db::Box &box) { if (! box.empty () && box.width () > 0 && box.height () > 0) { - m_polygons.insert (db::Polygon (box)); - m_is_merged = false; - invalidate_cache (); + + if (empty ()) { + + m_polygons.insert (db::Polygon (box)); + m_is_merged = true; + update_bbox (box); + + } else { + + m_polygons.insert (db::Polygon (box)); + m_is_merged = false; + invalidate_cache (); + + } + } } @@ -2283,6 +2326,29 @@ Region::iter () const return *(i ? i : &def_iter); } +void +Region::set_delegate (RegionDelegate *delegate) +{ + if (delegate != mp_delegate) { + delete mp_delegate; + mp_delegate = delegate; + } +} + +FlatRegion * +Region::flat_region () +{ + FlatRegion *region = dynamic_cast (mp_delegate); + if (! region) { + region = new FlatRegion (); + region->RegionDelegate::operator= (*mp_delegate); + region->insert_seq (begin ()); + set_delegate (region); + } + + return region; +} + // ------------------------------------------------------------------------------------------------------------- #if 0 diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 269532762..c93b943de 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -429,6 +429,9 @@ public: RegionDelegate (); virtual ~RegionDelegate (); + RegionDelegate (const RegionDelegate &other); + RegionDelegate &operator= (const RegionDelegate &other); + virtual RegionDelegate *clone () const = 0; void enable_progress (const std::string &progress_desc); @@ -632,7 +635,7 @@ public: virtual RegionDelegate *rounded_corners (double, double, unsigned int) const { return new EmptyRegion (); } virtual RegionDelegate *smoothed (coord_type) const { return new EmptyRegion (); } - virtual bool has_valid_polygons () const { return false; } + virtual bool has_valid_polygons () const { return true; } virtual const db::Polygon *nth (size_t) const { tl_assert (false); } virtual const db::RecursiveShapeIterator *iter () const { return 0; } @@ -928,14 +931,17 @@ public: } } - db::Shapes &raw_polygons () { return m_polygons; } - protected: virtual void merged_semantics_changed (); virtual Box compute_bbox () const; void invalidate_cache (); + void set_is_merged (bool m); private: + friend class AsIfFlatRegion; + + db::Shapes &raw_polygons () { return m_polygons; } + FlatRegion &operator= (const FlatRegion &other); bool m_is_merged; @@ -2195,25 +2201,8 @@ public: private: RegionDelegate *mp_delegate; - void set_delegate (RegionDelegate *delegate) - { - if (delegate != mp_delegate) { - delete mp_delegate; - mp_delegate = delegate; - } - } - - FlatRegion *flat_region () - { - FlatRegion *region = dynamic_cast (mp_delegate); - if (! region) { - region = new FlatRegion (); - region->insert_seq (begin ()); - set_delegate (region); - } - - return region; - } + void set_delegate (RegionDelegate *delegate); + FlatRegion *flat_region (); }; diff --git a/src/db/unit_tests/dbRegion.cc b/src/db/unit_tests/dbRegion.cc index c1eb0271b..aca7b2640 100644 --- a/src/db/unit_tests/dbRegion.cc +++ b/src/db/unit_tests/dbRegion.cc @@ -52,10 +52,19 @@ TEST(1) EXPECT_EQ (r.transformed (db::Trans (db::Vector (1, 2))).to_string (), "(1,2;1,202;101,202;101,2)"); EXPECT_EQ (r.bbox ().to_string (), "(0,0;100,200)"); EXPECT_EQ (r.empty (), false); - EXPECT_EQ (r.is_merged (), false); + EXPECT_EQ (r.is_merged (), true); EXPECT_EQ (r.is_box (), true); EXPECT_EQ (r.begin ().at_end (), false); + db::Region rr = r; + rr.insert (db::Box (db::Point (10, 10), db::Point (110, 30))); + EXPECT_EQ (rr.bbox ().to_string (), "(0,0;110,200)"); + EXPECT_EQ (rr.to_string (), "(0,0;0,200;100,200;100,0);(10,10;10,30;110,30;110,10)"); + EXPECT_EQ (rr.empty (), false); + EXPECT_EQ (rr.is_merged (), false); + EXPECT_EQ (rr.is_box (), false); + EXPECT_EQ (rr.begin ().at_end (), false); + db::Region r1 = r; db::Region r2; EXPECT_EQ (r1.to_string (), "(0,0;0,200;100,200;100,0)"); From 885a440089f5b43efa3978273be1c117d70690f9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 7 Nov 2018 02:44:15 +0100 Subject: [PATCH 039/335] Further refactoring: provide adressable polygons. --- src/db/db/dbEdges.cc | 13 ++++-- src/db/db/dbRegion.cc | 65 +++++++++++++++++++++-------- src/db/db/dbRegion.h | 96 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 20 deletions(-) diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 9d2fa8d39..42f770aff 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -724,8 +724,11 @@ Edges::select_interacting (const Region &other) scanner.insert ((char *) &*e, 0); } - for (Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { - scanner.insert ((char *) &*p + 1, 1); + AddressablePolygonDelivery p = other.addressable_polygons (); + + for ( ; ! p.at_end (); ++p) { + // NOTE: dirty hack - we can take the address of the polygon because we used ensure_flat_polygons. + scanner.insert ((char *) p.operator-> () + 1, 1); } Edges output; @@ -753,8 +756,10 @@ Edges::select_not_interacting (const Region &other) scanner.insert ((char *) &*e, 0); } - for (Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { - scanner.insert ((char *) &*p + 1, 1); + AddressablePolygonDelivery p = other.addressable_polygons (); + for ( ; ! p.at_end (); ++p) { + // NOTE: dirty hack - we can take the address of the polygon because we used ensure_flat_polygons. + scanner.insert ((char *) p.operator-> () + 1, 1); } std::set interacting; diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index c63ae42a5..439772415 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -559,8 +559,10 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse) db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size () + other.size ()); - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - scanner.insert ((char *) &*p + 1, 1); + AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ()); + + for ( ; ! p.at_end (); ++p) { + scanner.insert ((char *) p.operator-> () + 1, 1); } other.ensure_valid_merged_edges (); @@ -1019,23 +1021,28 @@ public: } void finish (const db::Polygon *o, size_t p) + { + enter (*o, p); + } + + void enter (const db::Polygon &o, size_t p) { if (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()) { // finally we check the polygons vs. itself for checks involving intra-polygon interactions m_scanner.clear (); - m_scanner.reserve (o->vertices ()); + m_scanner.reserve (o.vertices ()); m_edges.clear (); - m_edges.reserve (o->vertices ()); + m_edges.reserve (o.vertices ()); - for (db::Polygon::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { + for (db::Polygon::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) { m_edges.push_back (*e); m_scanner.insert (& m_edges.back (), p); } - tl_assert (m_edges.size () == o->vertices ()); + tl_assert (m_edges.size () == o.vertices ()); m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); @@ -1043,26 +1050,31 @@ public: } void add (const db::Polygon *o1, size_t p1, const db::Polygon *o2, size_t p2) + { + enter (*o1, p1, *o2, p2); + } + + void enter (const db::Polygon &o1, size_t p1, const db::Polygon &o2, size_t p2) { if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { m_scanner.clear (); - m_scanner.reserve (o1->vertices () + o2->vertices ()); + m_scanner.reserve (o1.vertices () + o2.vertices ()); m_edges.clear (); - m_edges.reserve (o1->vertices () + o2->vertices ()); + m_edges.reserve (o1.vertices () + o2.vertices ()); - for (db::Polygon::polygon_edge_iterator e = o1->begin_edge (); ! e.at_end (); ++e) { + for (db::Polygon::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) { m_edges.push_back (*e); m_scanner.insert (& m_edges.back (), p1); } - for (db::Polygon::polygon_edge_iterator e = o2->begin_edge (); ! e.at_end (); ++e) { + for (db::Polygon::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) { m_edges.push_back (*e); m_scanner.insert (& m_edges.back (), p2); } - tl_assert (m_edges.size () == o1->vertices () + o2->vertices ()); + tl_assert (m_edges.size () == o1.vertices () + o2.vertices ()); // temporarily disable intra-polygon check in that step .. we do that later in finish() // if required (#650). @@ -1092,18 +1104,26 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size () + (other ? other->size () : 0)); + AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ()); + size_t n = 0; - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - scanner.insert (&*p, n); + for ( ; ! p.at_end (); ++p) { + scanner.insert (p.operator-> (), n); n += 2; } + AddressablePolygonDelivery po; + if (other) { + + po = other->addressable_merged_polygons (); + n = 1; - for (RegionIterator p (other->begin_merged ()); ! p.at_end (); ++p) { - scanner.insert (&*p, n); + for ( ; ! po.at_end (); ++po) { + scanner.insert (po.operator-> (), n); n += 2; } + } EdgeRelationFilter check (rel, d, metrics); @@ -1138,11 +1158,13 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord Poly2PolyCheck poly_check (edge_check); do { + size_t n = 0; for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - poly_check.finish (&*p, n); + poly_check.enter (*p, n); n += 2; } + } while (edge_check.prepare_next_pass ()); return result; @@ -1966,6 +1988,11 @@ bool FlatRegion::has_valid_polygons () const return true; } +bool FlatRegion::has_valid_merged_polygons () const +{ + return true; +} + const db::RecursiveShapeIterator *FlatRegion::iter () const { return 0; @@ -2205,6 +2232,12 @@ OriginalLayerRegion::has_valid_polygons () const return false; } +bool +OriginalLayerRegion::has_valid_merged_polygons () const +{ + return merged_semantics () && ! m_is_merged; +} + const db::RecursiveShapeIterator * OriginalLayerRegion::iter () const { diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index c93b943de..1ab534af0 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -41,6 +41,8 @@ #include "tlString.h" #include "gsiObject.h" +#include + namespace db { class EdgeFilterBase; @@ -526,6 +528,7 @@ public: virtual const db::Polygon *nth (size_t n) const = 0; virtual bool has_valid_polygons () const = 0; + virtual bool has_valid_merged_polygons () const = 0; virtual const db::RecursiveShapeIterator *iter () const = 0; @@ -636,6 +639,7 @@ public: virtual RegionDelegate *smoothed (coord_type) const { return new EmptyRegion (); } virtual bool has_valid_polygons () const { return true; } + virtual bool has_valid_merged_polygons () const { return true; } virtual const db::Polygon *nth (size_t) const { tl_assert (false); } virtual const db::RecursiveShapeIterator *iter () const { return 0; } @@ -883,6 +887,7 @@ public: virtual const db::Polygon *nth (size_t n) const; virtual bool has_valid_polygons () const; + virtual bool has_valid_merged_polygons () const; virtual const db::RecursiveShapeIterator *iter () const; @@ -980,6 +985,7 @@ public: virtual const db::Polygon *nth (size_t n) const; virtual bool has_valid_polygons () const; + virtual bool has_valid_merged_polygons () const; virtual const db::RecursiveShapeIterator *iter () const; @@ -999,6 +1005,61 @@ private: void ensure_merged_polygons_valid () const; }; +/** + * @brief A helper class allowing delivery of addressable polygons + * + * In some applications (i.e. box scanner), polygons need to be taken + * by address. The region cannot always deliver adressable polygons. + * This class help providing this ability by keeping a temporary copy + * if required. + */ + +class DB_PUBLIC AddressablePolygonDelivery +{ +public: + AddressablePolygonDelivery () + : m_iter (), m_valid (false) + { + // .. nothing yet .. + } + + AddressablePolygonDelivery (const RegionIterator &iter, bool valid) + : m_iter (iter), m_valid (valid) + { + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + } + + bool at_end () const + { + return m_iter.at_end (); + } + + AddressablePolygonDelivery &operator++ () + { + ++m_iter; + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + return *this; + } + + const db::Polygon *operator-> () const + { + if (m_valid) { + return m_iter.operator-> (); + } else { + return &m_heap.back (); + } + } + +private: + RegionIterator m_iter; + bool m_valid; + std::list m_heap; +}; + /** * @brief A region * @@ -2161,12 +2222,47 @@ public: /** * @brief Returns true, if the region has valid polygons stored within itself + * + * If the region has valid polygons, it is permissable to use the polygon's addresses + * from the iterator. Furthermore, the random access operator nth() is available. */ bool has_valid_polygons () const { return mp_delegate->has_valid_polygons (); } + /** + * @brief Returns an addressable delivery for polygons + * + * This object allows accessing the polygons by address, even if they + * are not delivered from a container. The magic is a heap object + * inside the delivery object. Hence, the deliver object must persist + * as long as the addresses are required. + */ + AddressablePolygonDelivery addressable_polygons () const + { + return AddressablePolygonDelivery (begin (), has_valid_polygons ()); + } + + /** + * @brief Returns true, if the region has valid merged polygons stored within itself + * + * If the region has valid merged polygons, it is permissable to use the polygon's addresses + * from the merged polygon iterator. Furthermore, the random access operator nth() is available. + */ + bool has_valid_merged_polygons () const + { + return mp_delegate->has_valid_merged_polygons (); + } + + /** + * @brief Returns an addressable delivery for merged polygons + */ + AddressablePolygonDelivery addressable_merged_polygons () const + { + return AddressablePolygonDelivery (begin_merged (), has_valid_merged_polygons ()); + } + /** * @brief Gets the internal iterator * From 8107e1bb516efea2de3e792c19c17921f9395b68 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 7 Nov 2018 22:17:51 +0100 Subject: [PATCH 040/335] Refactoring: separated sources for db::Region --- src/db/db/db.pro | 14 +- src/db/db/dbAsIfFlatRegion.cc | 1492 +++++++++ src/db/db/dbAsIfFlatRegion.h | 226 ++ src/db/db/dbEdges.cc | 1 + src/db/db/dbEmptyRegion.cc | 98 + src/db/db/dbEmptyRegion.h | 130 + src/db/db/dbFlatRegion.cc | 432 +++ src/db/db/dbFlatRegion.h | 197 ++ src/db/db/dbOriginalLayerRegion.cc | 274 ++ src/db/db/dbOriginalLayerRegion.h | 83 + src/db/db/dbRegion.cc | 3909 +----------------------- src/db/db/dbRegion.h | 1877 +----------- src/db/db/dbRegionDelegate.cc | 91 + src/db/db/dbRegionDelegate.h | 206 ++ src/db/unit_tests/dbTilingProcessor.cc | 1 + 15 files changed, 3310 insertions(+), 5721 deletions(-) create mode 100644 src/db/db/dbAsIfFlatRegion.cc create mode 100644 src/db/db/dbAsIfFlatRegion.h create mode 100644 src/db/db/dbEmptyRegion.cc create mode 100644 src/db/db/dbEmptyRegion.h create mode 100644 src/db/db/dbFlatRegion.cc create mode 100644 src/db/db/dbFlatRegion.h create mode 100644 src/db/db/dbOriginalLayerRegion.cc create mode 100644 src/db/db/dbOriginalLayerRegion.h create mode 100644 src/db/db/dbRegionDelegate.cc create mode 100644 src/db/db/dbRegionDelegate.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 8c0a4e558..788335b11 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -119,7 +119,12 @@ SOURCES = \ gsiDeclDbVector.cc \ gsiDeclDbLayoutDiff.cc \ gsiDeclDbGlyphs.cc \ - dbConverters.cc + dbConverters.cc \ + dbAsIfFlatRegion.cc \ + dbEmptyRegion.cc \ + dbFlatRegion.cc \ + dbOriginalLayerRegion.cc \ + dbRegionDelegate.cc HEADERS = \ dbArray.h \ @@ -208,7 +213,12 @@ HEADERS = \ dbForceLink.h \ dbPlugin.h \ dbInit.h \ - dbConverters.h + dbConverters.h \ + dbAsIfFlatRegion.h \ + dbEmptyRegion.h \ + dbFlatRegion.h \ + dbOriginalLayerRegion.h \ + dbRegionDelegate.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc new file mode 100644 index 000000000..3f4b41003 --- /dev/null +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -0,0 +1,1492 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbAsIfFlatRegion.h" +#include "dbFlatRegion.h" +#include "dbEmptyRegion.h" +#include "dbRegion.h" +#include "dbShapeProcessor.h" +#include "dbBoxConvert.h" +#include "dbBoxScanner.h" +#include "dbClip.h" +#include "dbPolygonTools.h" + +#include + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// AsIfFlagRegion implementation + +AsIfFlatRegion::AsIfFlatRegion () + : RegionDelegate (), m_bbox_valid (false) +{ + // .. nothing yet .. +} + +AsIfFlatRegion::~AsIfFlatRegion () +{ + // .. nothing yet .. +} + +std::string +AsIfFlatRegion::to_string (size_t nmax) const +{ + std::ostringstream os; + RegionIterator p (begin ()); + bool first = true; + for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { + if (! first) { + os << ";"; + } + first = false; + os << p->to_string (); + } + if (! p.at_end ()) { + os << "..."; + } + return os.str (); +} + +Edges +AsIfFlatRegion::edges (const EdgeFilterBase *filter) const +{ + Edges edges; + + size_t n = 0; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + edges.reserve (n); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected (*e)) { + edges.insert (*e); + } + } + } + + return edges; +} + +RegionDelegate * +AsIfFlatRegion::hulls () const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + db::Polygon h; + h.assign_hull (p->begin_hull (), p->end_hull ()); + new_region->insert (h); + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::holes () const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + for (size_t i = 0; i < p->holes (); ++i) { + db::Polygon h; + h.assign_hull (p->begin_hole ((unsigned int) i), p->end_hole ((unsigned int) i)); + new_region->insert (h); + } + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::rounded_corners (double rinner, double router, unsigned int n) const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + new_region->insert (db::compute_rounded (*p, rinner, router, n)); + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::smoothed (coord_type d) const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + new_region->insert (db::smooth (*p, d)); + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::in (const Region &other, bool invert) const +{ + std::set op; + for (RegionIterator o (other.begin_merged ()); ! o.at_end (); ++o) { + op.insert (*o); + } + + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator o (begin_merged ()); ! o.at_end (); ++o) { + if ((op.find (*o) == op.end ()) == invert) { + new_region->insert (*o); + } + } + + return new_region.release (); +} + +size_t +AsIfFlatRegion::size () const +{ + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + ++n; + } + return n; +} + +bool +AsIfFlatRegion::is_box () const +{ + RegionIterator p (begin ()); + if (p.at_end ()) { + return false; + } else { + const db::Polygon &poly = *p; + ++p; + if (! p.at_end ()) { + return false; + } else { + return poly.is_box (); + } + } +} + +AsIfFlatRegion::area_type +AsIfFlatRegion::area (const db::Box &box) const +{ + area_type a = 0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (box.empty () || p->box ().inside (box)) { + a += p->area (); + } else { + std::vector clipped; + clip_poly (*p, box, clipped); + for (std::vector::const_iterator c = clipped.begin (); c != clipped.end (); ++c) { + a += c->area (); + } + } + } + + return a; +} + +AsIfFlatRegion::perimeter_type +AsIfFlatRegion::perimeter (const db::Box &box) const +{ + perimeter_type d = 0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + if (box.empty () || p->box ().inside (box)) { + d += p->perimeter (); + } else { + + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + + if (box.empty ()) { + d += (*e).length (); + } else { + + std::pair ce = (*e).clipped (box); + if (ce.first) { + + db::Coord dx = ce.second.dx (); + db::Coord dy = ce.second.dy (); + db::Coord x = ce.second.p1 ().x (); + db::Coord y = ce.second.p1 ().y (); + if ((dx == 0 && x == box.left () && dy < 0) || + (dx == 0 && x == box.right () && dy > 0) || + (dy == 0 && y == box.top () && dx < 0) || + (dy == 0 && y == box.bottom () && dx > 0)) { + // not counted -> box is at outside side of the edge + } else { + d += ce.second.length (); + } + + } + + } + + } + + } + + } + + return d; +} + +Box AsIfFlatRegion::bbox () const +{ + if (! m_bbox_valid) { + m_bbox = compute_bbox (); + m_bbox_valid = true; + } + return m_bbox; +} + +Box AsIfFlatRegion::compute_bbox () const +{ + db::Box b; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + b += p->box (); + } + return b; +} + +void AsIfFlatRegion::update_bbox (const db::Box &b) +{ + m_bbox = b; + m_bbox_valid = true; +} + +void AsIfFlatRegion::invalidate_bbox () +{ + m_bbox_valid = false; +} + +RegionDelegate * +AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const +{ + std::auto_ptr new_region (new FlatRegion ()); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_region->insert (*p); + } + } + + return new_region.release (); +} + +namespace +{ + +/** + * @brief A helper class for the region to edge interaction functionality + * + * Note: This special scanner uses pointers to two different objects: edges and polygons. + * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate + * pointers to edges. + * + * There is a special box converter which is able to sort that out as well. + */ +template +class region_to_edge_interaction_filter + : public db::box_scanner_receiver +{ +public: + region_to_edge_interaction_filter (OutputContainer &output) + : mp_output (&output), m_inverse (false) + { + // .. nothing yet .. + } + + region_to_edge_interaction_filter (OutputContainer &output, const db::RegionIterator &polygons) + : mp_output (&output), m_inverse (true) + { + for (db::RegionIterator p = polygons; ! p.at_end (); ++p) { + m_seen.insert (&*p); + } + } + + void add (const char *o1, size_t p1, const char *o2, size_t p2) + { + const db::Edge *e = 0; + const db::Polygon *p = 0; + + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + if (p1 == 0 && p2 == 1) { + e = reinterpret_cast (o1); + p = reinterpret_cast (o2 - 1); + } else if (p1 == 1 && p2 == 0) { + e = reinterpret_cast (o2); + p = reinterpret_cast (o1 - 1); + } + + if (e && p && (m_seen.find (p) == m_seen.end ()) != m_inverse) { + + // A polygon and an edge interact if the edge is either inside completely + // of at least one edge of the polygon intersects with the edge + bool interacts = false; + if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) { + interacts = true; + } else { + for (db::Polygon::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) { + if ((*pe).intersect (*e)) { + interacts = true; + } + } + } + + if (interacts) { + if (m_inverse) { + m_seen.erase (p); + } else { + m_seen.insert (p); + mp_output->insert (*p); + } + } + + } + } + + void fill_output () + { + for (std::set::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) { + mp_output->insert (**p); + } + } + +private: + OutputContainer *mp_output; + std::set m_seen; + bool m_inverse; +}; + +/** + * @brief A special box converter that splits the pointers into polygon and edge pointers + */ +struct EdgeOrRegionBoxConverter +{ + typedef db::Box box_type; + + db::Box operator() (const char &c) const + { + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + const char *cp = &c; + if ((size_t (cp) & 1) == 1) { + // it's a polygon + return (reinterpret_cast (cp - 1))->box (); + } else { + // it's an edge + const db::Edge *e = reinterpret_cast (cp); + return db::Box (e->p1 (), e->p2 ()); + } + } +}; + +} + +RegionDelegate * +AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse) const +{ + if (other.empty ()) { + if (! inverse) { + return new EmptyRegion (); + } else { + return clone (); + } + } else if (empty ()) { + return clone (); + } + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + other.size ()); + + AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ()); + + for ( ; ! p.at_end (); ++p) { + scanner.insert ((char *) p.operator-> () + 1, 1); + } + + other.ensure_valid_merged_edges (); + for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { + scanner.insert ((char *) &*e, 0); + } + + std::auto_ptr output (new FlatRegion (false)); + EdgeOrRegionBoxConverter bc; + + if (! inverse) { + region_to_edge_interaction_filter filter (output->raw_polygons ()); + scanner.process (filter, 1, bc); + } else { + region_to_edge_interaction_filter filter (output->raw_polygons (), RegionIterator (begin_merged ())); + scanner.process (filter, 1, bc); + filter.fill_output (); + } + + return output.release (); +} + +RegionDelegate * +AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const +{ + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // shortcut + if (empty ()) { + return clone (); + } else if (other.empty ()) { + // clear, if b is empty and + // * mode is inside or interacting and inverse is false ("inside" or "interacting") + // * mode is outside and inverse is true ("not outside") + if ((mode <= 0) != inverse) { + return new EmptyRegion (); + } else { + return clone (); + } + } + + for (RegionIterator p = other.begin (); ! p.at_end (); ++p) { + if (p->box ().touches (bbox ())) { + ep.insert (*p, 0); + } + } + + size_t n = 1; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) { + if (mode > 0 || p->box ().touches (other.bbox ())) { + ep.insert (*p, n); + } + } + + db::InteractionDetector id (mode, 0); + id.set_include_touching (touching); + db::EdgeSink es; + ep.process (es, id); + id.finish (); + + std::auto_ptr output (new FlatRegion (false)); + + n = 0; + std::set selected; + for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { + ++n; + selected.insert (i->second); + } + + output->reserve (n); + + n = 1; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) { + if ((selected.find (n) == selected.end ()) == inverse) { + output->raw_polygons ().insert (*p); + } + } + + return output.release (); +} + +EdgePairs +AsIfFlatRegion::grid_check (db::Coord gx, db::Coord gy) const +{ + EdgePairs out; + + gx = std::max (db::Coord (1), gx); + gy = std::max (db::Coord (1), gy); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + for (size_t i = 0; i < p->holes () + 1; ++i) { + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = p->begin_hull (); + e = p->end_hull (); + } else { + b = p->begin_hole ((unsigned int) (i - 1)); + e = p->end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + if (((*pt).x () % gx) != 0 || ((*pt).y () % gy) != 0) { + out.insert (EdgePair (db::Edge (*pt, *pt), db::Edge (*pt, *pt))); + } + } + + } + + } + + return out; +} + +static bool ac_less (double cos_a, bool gt180_a, double cos_b, bool gt180_b) +{ + if (gt180_a != gt180_b) { + return gt180_a < gt180_b; + } else { + if (gt180_a) { + return cos_a < cos_b - 1e-10; + } else { + return cos_a > cos_b + 1e-10; + } + } +} + +EdgePairs +AsIfFlatRegion::angle_check (double min, double max, bool inverse) const +{ + EdgePairs out; + + double cos_min = cos (std::max (0.0, std::min (360.0, min)) / 180.0 * M_PI); + double cos_max = cos (std::max (0.0, std::min (360.0, max)) / 180.0 * M_PI); + bool gt180_min = min > 180.0; + bool gt180_max = max > 180.0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + for (size_t i = 0; i < p->holes () + 1; ++i) { + + const db::Polygon::contour_type *h = 0; + if (i == 0) { + h = &p->hull (); + } else { + h = &p->hole ((unsigned int) (i - 1)); + } + + size_t np = h->size (); + + for (size_t j = 0; j < np; ++j) { + + db::Edge e ((*h) [j], (*h) [(j + 1) % np]); + db::Edge ee (e.p2 (), (*h) [(j + 2) % np]); + double le = e.double_length (); + double lee = ee.double_length (); + + double cos_a = -db::sprod (e, ee) / (le * lee); + bool gt180_a = db::vprod_sign (e, ee) > 0; + + if ((ac_less (cos_a, gt180_a, cos_max, gt180_max) && !ac_less (cos_a, gt180_a, cos_min, gt180_min)) == !inverse) { + out.insert (EdgePair (e, ee)); + } + + } + + } + + } + + return out; +} + +static inline db::Coord snap_to_grid (db::Coord c, db::Coord g) +{ + // This form of snapping always snaps g/2 to right/top. + if (c < 0) { + c = -g * ((-c + (g - 1) / 2) / g); + } else { + c = g * ((c + g / 2) / g); + } + return c; +} + +RegionDelegate * +AsIfFlatRegion::snapped (db::Coord gx, db::Coord gy) +{ + std::auto_ptr new_region (new FlatRegion (merged_semantics ())); + + gx = std::max (db::Coord (1), gx); + gy = std::max (db::Coord (1), gy); + + std::vector pts; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + db::Polygon pnew; + + for (size_t i = 0; i < p->holes () + 1; ++i) { + + pts.clear (); + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = p->begin_hull (); + e = p->end_hull (); + } else { + b = p->begin_hole ((unsigned int) (i - 1)); + e = p->end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + pts.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); + } + + if (i == 0) { + pnew.assign_hull (pts.begin (), pts.end ()); + } else { + pnew.insert_hole (pts.begin (), pts.end ()); + } + + } + + new_region->raw_polygons ().insert (pnew); + + } + + return new_region.release (); +} + +namespace +{ + /** + * @brief A helper class to implement the strange polygon detector + */ + struct StrangePolygonInsideFunc + { + inline bool operator() (int wc) const + { + return wc < 0 || wc > 1; + } + }; +} + +RegionDelegate * +AsIfFlatRegion::strange_polygon_check () const +{ + EdgeProcessor ep; + std::auto_ptr new_region (new FlatRegion (merged_semantics ())); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + + ep.clear (); + ep.insert (*p); + + StrangePolygonInsideFunc inside; + db::GenericMerge op (inside); + db::ShapeGenerator pc (new_region->raw_polygons (), false); + db::PolygonGenerator pg (pc, false, false); + ep.process (pg, op); + } + + return new_region.release (); +} + + +namespace { + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +class Edge2EdgeCheck + : public db::box_scanner_receiver +{ +public: + Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool different_polygons, bool requires_different_layers) + : mp_check (&check), mp_output (&output), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), + m_pass (0) + { + m_distance = check.distance (); + } + + bool prepare_next_pass () + { + ++m_pass; + + if (m_pass == 1) { + + if (! m_ep.empty ()) { + m_ep_discarded.resize (m_ep.size (), false); + return true; + } + + } else if (m_pass == 2) { + + std::vector::const_iterator d = m_ep_discarded.begin (); + std::vector::const_iterator ep = m_ep.begin (); + while (ep != m_ep.end ()) { + tl_assert (d != m_ep_discarded.end ()); + if (! *d) { + mp_output->insert (*ep); + } + ++d; + ++ep; + } + + } + + return false; + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + if (m_pass == 0) { + + // Overlap or inside checks require input from different layers + if ((! m_different_polygons || p1 != p2) && (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0)) { + + // ensure that the first check argument is of layer 1 and the second of + // layer 2 (unless both are of the same layer) + int l1 = int (p1 & size_t (1)); + int l2 = int (p2 & size_t (1)); + + db::EdgePair ep; + if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { + + // found a violation: store inside the local buffer for now. In the second + // pass we will eliminate those which are shielded completely. + size_t n = m_ep.size (); + m_ep.push_back (ep); + m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n)); + m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n)); + + } + + } + + } else { + + // a simple (complete) shielding implementation which is based on the + // assumption that shielding is relevant as soon as a foreign edge cuts through + // both of the edge pair's connecting edges. + + // TODO: this implementation does not take into account the nature of the + // EdgePair - because of "whole_edge" it may not reflect the part actually + // violating the distance. + + std::vector n1, n2; + + for (unsigned int p = 0; p < 2; ++p) { + + std::pair k (*o1, p1); + for (std::multimap, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) { + n1.push_back (i->second); + } + + std::sort (n1.begin (), n1.end ()); + + std::swap (o1, o2); + std::swap (p1, p2); + n1.swap (n2); + + } + + for (unsigned int p = 0; p < 2; ++p) { + + std::vector nn; + std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); + + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + if (! m_ep_discarded [*i]) { + db::EdgePair ep = m_ep [*i].normalized (); + if (db::Edge (ep.first ().p1 (), ep.second ().p2 ()).intersect (*o2) && + db::Edge (ep.second ().p1 (), ep.first ().p2 ()).intersect (*o2)) { + m_ep_discarded [*i] = true; + } + } + } + + std::swap (o1, o2); + std::swap (p1, p2); + n1.swap (n2); + + } + + } + + } + + /** + * @brief Gets a value indicating whether the check requires different layers + */ + bool requires_different_layers () const + { + return m_requires_different_layers; + } + + /** + * @brief Sets a value indicating whether the check requires different layers + */ + void set_requires_different_layers (bool f) + { + m_requires_different_layers = f; + } + + /** + * @brief Gets a value indicating whether the check requires different layers + */ + bool different_polygons () const + { + return m_different_polygons; + } + + /** + * @brief Sets a value indicating whether the check requires different layers + */ + void set_different_polygons (bool f) + { + m_different_polygons = f; + } + + /** + * @brief Gets the distance value + */ + EdgeRelationFilter::distance_type distance () const + { + return m_distance; + } + +private: + const EdgeRelationFilter *mp_check; + EdgePairs *mp_output; + bool m_requires_different_layers; + bool m_different_polygons; + EdgeRelationFilter::distance_type m_distance; + std::vector m_ep; + std::multimap, size_t> m_e2ep; + std::vector m_ep_discarded; + unsigned int m_pass; +}; + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +class Poly2PolyCheck + : public db::box_scanner_receiver +{ +public: + Poly2PolyCheck (Edge2EdgeCheck &output) + : mp_output (&output) + { + // .. nothing yet .. + } + + void finish (const db::Polygon *o, size_t p) + { + enter (*o, p); + } + + void enter (const db::Polygon &o, size_t p) + { + if (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()) { + + // finally we check the polygons vs. itself for checks involving intra-polygon interactions + + m_scanner.clear (); + m_scanner.reserve (o.vertices ()); + + m_edges.clear (); + m_edges.reserve (o.vertices ()); + + for (db::Polygon::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p); + } + + tl_assert (m_edges.size () == o.vertices ()); + + m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); + + } + } + + void add (const db::Polygon *o1, size_t p1, const db::Polygon *o2, size_t p2) + { + enter (*o1, p1, *o2, p2); + } + + void enter (const db::Polygon &o1, size_t p1, const db::Polygon &o2, size_t p2) + { + if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { + + m_scanner.clear (); + m_scanner.reserve (o1.vertices () + o2.vertices ()); + + m_edges.clear (); + m_edges.reserve (o1.vertices () + o2.vertices ()); + + for (db::Polygon::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p1); + } + + for (db::Polygon::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p2); + } + + tl_assert (m_edges.size () == o1.vertices () + o2.vertices ()); + + // temporarily disable intra-polygon check in that step .. we do that later in finish() + // if required (#650). + bool no_intra = mp_output->different_polygons (); + mp_output->set_different_polygons (true); + + m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); + + mp_output->set_different_polygons (no_intra); + + } + } + +private: + db::box_scanner m_scanner; + Edge2EdgeCheck *mp_output; + std::vector m_edges; +}; + +} + +EdgePairs +AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + EdgePairs result; + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + (other ? other->size () : 0)); + + AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ()); + + size_t n = 0; + for ( ; ! p.at_end (); ++p) { + scanner.insert (p.operator-> (), n); + n += 2; + } + + AddressablePolygonDelivery po; + + if (other) { + + po = other->addressable_merged_polygons (); + + n = 1; + for ( ; ! po.at_end (); ++po) { + scanner.insert (po.operator-> (), n); + n += 2; + } + + } + + EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (other != 0); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + Edge2EdgeCheck edge_check (check, result, different_polygons, other != 0); + Poly2PolyCheck poly_check (edge_check); + + do { + scanner.process (poly_check, d, db::box_convert ()); + } while (edge_check.prepare_next_pass ()); + + return result; +} + +EdgePairs +AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + EdgePairs result; + + EdgeRelationFilter check (rel, d, metrics); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + Edge2EdgeCheck edge_check (check, result, false, false); + Poly2PolyCheck poly_check (edge_check); + + do { + + size_t n = 0; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + poly_check.enter (*p, n); + n += 2; + } + + } while (edge_check.prepare_next_pass ()); + + return result; +} + +RegionDelegate * +AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc) const +{ + if (empty ()) { + + return new EmptyRegion (); + + } else if (is_box ()) { + + // take box only if min_wc == 0, otherwise clear + if (min_wc > 0) { + return new EmptyRegion (); + } else { + return clone (); + } + + } else { + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + + // and run the merge step + db::MergeOp op (min_wc); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::region_from_box (const db::Box &b) +{ + if (! b.empty () && b.width () > 0 && b.height () > 0) { + FlatRegion *new_region = new FlatRegion (); + new_region->insert (b); + return new_region; + } else { + return new EmptyRegion (); + } +} + +RegionDelegate * +AsIfFlatRegion::sized (coord_type d, unsigned int mode) const +{ + return sized (d, d, mode); +} + +RegionDelegate * +AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const +{ + if (empty ()) { + + // ignore empty + return new EmptyRegion (); + + } else if (is_box () && mode >= 2) { + + // simplified handling for a box + db::Box b = bbox ().enlarged (db::Vector (dx, dy)); + return region_from_box (b); + + } else if (! merged_semantics ()) { + + // Generic case + std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); + + db::ShapeGenerator pc (new_region->raw_polygons (), false); + db::PolygonGenerator pg (pc, false, true); + db::SizingPolygonFilter sf (pg, dx, dy, mode); + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + sf.put (*p); + } + + return new_region.release (); + + } else { + + // Generic case - the size operation will merge first + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); + db::SizingPolygonFilter siz (pg2, dx, dy, mode); + db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/); + db::BooleanOp op (db::BooleanOp::Or); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::and_with (const Region &other) const +{ + if (empty () || other.empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (is_box () && other.is_box ()) { + + // Simplified handling for boxes + db::Box b = bbox (); + b &= other.bbox (); + return region_from_box (b); + + } else if (is_box () && ! other.strict_handling ()) { + + // map AND with box to clip .. + db::Box b = bbox (); + std::auto_ptr new_region (new FlatRegion (false)); + + std::vector clipped; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + clipped.clear (); + clip_poly (*p, b, clipped); + new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + } + + return new_region.release (); + + } else if (other.is_box () && ! strict_handling ()) { + + // map AND with box to clip .. + db::Box b = other.bbox (); + std::auto_ptr new_region (new FlatRegion (false)); + + std::vector clipped; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + clipped.clear (); + clip_poly (*p, b, clipped); + new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + } + + return new_region.release (); + + } else if (! bbox ().overlaps (other.bbox ())) { + + // Result will be nothing + return new EmptyRegion (); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::And); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::not_with (const Region &other) const +{ + if (empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (other.empty () && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::ANotB); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::xor_with (const Region &other) const +{ + if (empty () && ! other.strict_handling ()) { + + return other.delegate ()->clone (); + + } else if (other.empty () && ! strict_handling ()) { + + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { + + // Simplified handling for disjunct case + return or_with (other); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::Xor); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::or_with (const Region &other) const +{ + if (empty () && ! other.strict_handling ()) { + + return other.delegate ()->clone (); + + } else if (other.empty () && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { + + // Simplified handling for disjunct case + return add (other); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::Or); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::add (const Region &other) const +{ + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + std::auto_ptr new_region (new FlatRegion (*other_flat)); + new_region->set_is_merged (false); + new_region->invalidate_cache (); + + size_t n = new_region->raw_polygons ().size () + size (); + + new_region->reserve (n); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + return new_region.release (); + + } else { + + std::auto_ptr new_region (new FlatRegion (false /*not merged*/)); + + size_t n = size () + other.size (); + + new_region->reserve (n); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + return new_region.release (); + + } +} + +bool +AsIfFlatRegion::equals (const Region &other) const +{ + if (empty () != other.empty ()) { + return false; + } + if (size () != other.size ()) { + return false; + } + RegionIterator o1 (begin ()); + RegionIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return false; + } + ++o1; + ++o2; + } + return true; +} + +bool +AsIfFlatRegion::less (const Region &other) const +{ + if (empty () != other.empty ()) { + return empty () < other.empty (); + } + if (size () != other.size ()) { + return (size () < other.size ()); + } + RegionIterator o1 (begin ()); + RegionIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return *o1 < *o2; + } + ++o1; + ++o2; + } + return false; +} + +} + diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h new file mode 100644 index 000000000..e9cedaaa8 --- /dev/null +++ b/src/db/db/dbAsIfFlatRegion.h @@ -0,0 +1,226 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbAsIfFlatRegion +#define HDR_dbAsIfFlatRegion + +#include "dbCommon.h" + +#include "dbRegionDelegate.h" + +namespace db { + +/** + * @brief Provides default flat implementations + */ +class DB_PUBLIC AsIfFlatRegion + : public RegionDelegate +{ +public: + AsIfFlatRegion (); + virtual ~AsIfFlatRegion (); + + virtual bool is_box () const; + virtual size_t size () const; + + virtual area_type area (const db::Box &box) const; + virtual perimeter_type perimeter (const db::Box &box) const; + virtual Box bbox () const; + + virtual std::string to_string (size_t nmax) const; + + EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const; + virtual EdgePairs angle_check (double min, double max, bool inverse) const; + + virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) + { + return snapped (gx, gy); + } + + virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); + + virtual Edges edges (const EdgeFilterBase *) const; + + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) + { + return filtered (filter); + } + + virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const; + + virtual RegionDelegate *merged_in_place () + { + return merged (); + } + + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc) + { + return merged (min_coherence, min_wc); + } + + virtual RegionDelegate *merged () const + { + return merged (min_coherence (), 0); + } + + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const; + + virtual RegionDelegate *strange_polygon_check () const; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; + + virtual RegionDelegate *and_with (const Region &other) const; + virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + + virtual RegionDelegate *add_in_place (const Region &other) + { + return add (other); + } + + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, false); + } + + virtual RegionDelegate *selected_not_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, true); + } + + virtual RegionDelegate *selected_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, false); + } + + virtual RegionDelegate *selected_not_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, true); + } + + virtual RegionDelegate *selected_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, false); + } + + virtual RegionDelegate *selected_not_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, true); + } + + virtual RegionDelegate *selected_interacting (const Edges &other) const + { + return selected_interacting_generic (other, false); + } + + virtual RegionDelegate *selected_not_interacting (const Edges &other) const + { + return selected_interacting_generic (other, true); + } + + virtual RegionDelegate *selected_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, false); + } + + virtual RegionDelegate *selected_not_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, true); + } + + virtual RegionDelegate *holes () const; + virtual RegionDelegate *hulls () const; + virtual RegionDelegate *in (const Region &other, bool invert) const; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; + virtual RegionDelegate *smoothed (coord_type d) const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + +protected: + void update_bbox (const db::Box &box); + void invalidate_bbox (); + +private: + AsIfFlatRegion &operator= (const AsIfFlatRegion &other); + + mutable bool m_bbox_valid; + mutable db::Box m_bbox; + + virtual db::Box compute_bbox () const; + static RegionDelegate *region_from_box (const db::Box &b); + + EdgePairs run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + EdgePairs run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; + RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const; +}; + +} + +#endif + diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 42f770aff..7ef4b2ffd 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -28,6 +28,7 @@ #include "dbBoxConvert.h" #include "dbBoxScanner.h" #include "dbPolygonTools.h" +#include "dbShapeProcessor.h" #include "tlIntervalMap.h" #include "tlVariant.h" diff --git a/src/db/db/dbEmptyRegion.cc b/src/db/db/dbEmptyRegion.cc new file mode 100644 index 000000000..6389faa51 --- /dev/null +++ b/src/db/db/dbEmptyRegion.cc @@ -0,0 +1,98 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbEmptyRegion.h" +#include "dbRegion.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// EmptyRegion implementation + +EmptyRegion::EmptyRegion () +{ + // .. nothing yet .. +} + +EmptyRegion::~EmptyRegion () +{ + // .. nothing yet .. +} + +EmptyRegion::EmptyRegion (const EmptyRegion &other) + : RegionDelegate (other) +{ + // .. nothing yet .. +} + +RegionDelegate * +EmptyRegion::clone () const +{ + return new EmptyRegion (*this); +} + +RegionDelegate * +EmptyRegion::add_in_place (const Region &other) +{ + return add (other); +} + +RegionDelegate * +EmptyRegion::add (const Region &other) const +{ + return other.delegate ()->clone (); +} + +RegionDelegate * +EmptyRegion::xor_with (const Region &other) const +{ + return or_with (other); +} + +RegionDelegate * +EmptyRegion::or_with (const Region &other) const +{ + if (other.empty ()) { + return new EmptyRegion (); + } else if (! other.strict_handling ()) { + return other.delegate ()->clone (); + } else { + return other.delegate ()->merged (); + } +} + +bool +EmptyRegion::equals (const Region &other) const +{ + return other.empty (); +} + +bool +EmptyRegion::less (const Region &other) const +{ + return other.empty () ? false : true; +} + +} + diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h new file mode 100644 index 000000000..696853af1 --- /dev/null +++ b/src/db/db/dbEmptyRegion.h @@ -0,0 +1,130 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbEmptyRegion +#define HDR_dbEmptyRegion + +#include "dbCommon.h" +#include "dbRegionDelegate.h" + +namespace db { + +/** + * @brief An empty Region + */ +class DB_PUBLIC EmptyRegion + : public RegionDelegate +{ +public: + EmptyRegion (); + virtual ~EmptyRegion (); + + EmptyRegion (const EmptyRegion &other); + RegionDelegate *clone () const; + + virtual RegionIteratorDelegate *begin () const { return 0; } + virtual RegionIteratorDelegate *begin_merged () const { return 0; } + + virtual std::pair begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + virtual std::pair begin_merged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + + virtual bool empty () const { return true; } + virtual size_t size () const { return 0; } + virtual std::string to_string (size_t) const { return std::string (); } + + virtual bool is_box () const { return false; } + virtual bool is_merged () const { return true; } + virtual area_type area (const db::Box &) const { return 0; } + virtual perimeter_type perimeter (const db::Box &) const { return 0; } + + virtual Box bbox () const { return Box (); } + + virtual EdgePairs width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs isolated_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs notch_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs enclosing_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs overlap_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs separation_check (const Region &, db::Coord, bool , metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs inside_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs grid_check (db::Coord, db::Coord) const { return EdgePairs (); } + virtual EdgePairs angle_check (double, double, bool) const { return EdgePairs (); } + + virtual RegionDelegate *snapped_in_place (db::Coord, db::Coord) { return this; } + virtual RegionDelegate *snapped (db::Coord, db::Coord) { return new EmptyRegion (); } + + virtual RegionDelegate *strange_polygon_check () const { return new EmptyRegion (); } + + virtual Edges edges (const EdgeFilterBase *) const { return db::Edges (); } + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; } + virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); } + + virtual RegionDelegate *merged_in_place () { return this; } + virtual RegionDelegate *merged_in_place (bool, unsigned int) { return this; } + virtual RegionDelegate *merged () const { return new EmptyRegion (); } + virtual RegionDelegate *merged (bool, unsigned int) const { return new EmptyRegion (); } + + virtual RegionDelegate *sized (coord_type, unsigned int) const { return new EmptyRegion (); } + virtual RegionDelegate *sized (coord_type, coord_type, unsigned int) const { return new EmptyRegion (); } + + virtual RegionDelegate *and_with (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *not_with (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + virtual RegionDelegate *add_in_place (const Region &other); + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_outside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_inside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_inside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_interacting (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_interacting (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_interacting (const Edges &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_interacting (const Edges &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_overlapping (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_overlapping (const Region &) const { return new EmptyRegion (); } + + virtual RegionDelegate *holes () const { return new EmptyRegion (); } + virtual RegionDelegate *hulls () const { return new EmptyRegion (); } + virtual RegionDelegate *in (const Region &, bool) const { return new EmptyRegion (); } + virtual RegionDelegate *rounded_corners (double, double, unsigned int) const { return new EmptyRegion (); } + virtual RegionDelegate *smoothed (coord_type) const { return new EmptyRegion (); } + + virtual bool has_valid_polygons () const { return true; } + virtual bool has_valid_merged_polygons () const { return true; } + virtual const db::Polygon *nth (size_t) const { tl_assert (false); } + + virtual const db::RecursiveShapeIterator *iter () const { return 0; } + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + +private: + EmptyRegion &operator= (const EmptyRegion &other); +}; + +} // namespace db + +#endif + diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc new file mode 100644 index 000000000..411dff8e5 --- /dev/null +++ b/src/db/db/dbFlatRegion.cc @@ -0,0 +1,432 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbFlatRegion.h" +#include "dbEmptyRegion.h" +#include "dbRegion.h" +#include "dbShapeProcessor.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// FlatRegion implementation + +FlatRegion::FlatRegion () + : AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false) +{ + init (); +} + +FlatRegion::~FlatRegion () +{ + // .. nothing yet .. +} + +FlatRegion::FlatRegion (const FlatRegion &other) + : AsIfFlatRegion (other), m_polygons (false), m_merged_polygons (false) +{ + init (); + + m_is_merged = other.m_is_merged; + m_polygons = other.m_polygons; + m_merged_polygons = other.m_merged_polygons; + m_merged_polygons_valid = other.m_merged_polygons_valid; +} + +FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) + : AsIfFlatRegion (), m_polygons (polygons), m_merged_polygons (false) +{ + init (); + + m_is_merged = is_merged; +} + +FlatRegion::FlatRegion (bool is_merged) + : AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false) +{ + init (); + + m_is_merged = is_merged; +} + +void FlatRegion::set_is_merged (bool m) +{ + m_is_merged = m; +} + +void FlatRegion::invalidate_cache () +{ + invalidate_bbox (); + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +void FlatRegion::init () +{ + m_is_merged = true; + m_merged_polygons_valid = false; +} + +void FlatRegion::merged_semantics_changed () +{ + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +void FlatRegion::reserve (size_t n) +{ + m_polygons.reserve (db::Polygon::tag (), n); +} + +void +FlatRegion::ensure_merged_polygons_valid () const +{ + if (! m_merged_polygons_valid) { + + m_merged_polygons.clear (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (0); + db::ShapeGenerator pc (m_merged_polygons); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + m_merged_polygons_valid = true; + + } +} + +RegionIteratorDelegate *FlatRegion::begin () const +{ + return new FlatRegionIterator (m_polygons.get_layer ().begin (), m_polygons.get_layer ().end ()); +} + +RegionIteratorDelegate *FlatRegion::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_polygons_valid (); + return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); + } +} + +std::pair FlatRegion::begin_iter () const +{ + return std::make_pair (db::RecursiveShapeIterator (m_polygons), db::ICplxTrans ()); +} + +std::pair FlatRegion::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_polygons_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); + } +} + +bool FlatRegion::empty () const +{ + return m_polygons.empty (); +} + +size_t FlatRegion::size () const +{ + return m_polygons.size (); +} + +bool FlatRegion::is_merged () const +{ + return m_is_merged; +} + +Box FlatRegion::compute_bbox () const +{ + m_polygons.update_bbox (); + return m_polygons.bbox (); +} + +RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter) +{ + polygon_iterator_type pw = m_polygons.get_layer ().begin (); + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + if (pw == m_polygons.get_layer ().end ()) { + m_polygons.get_layer ().insert (*p); + pw = m_polygons.get_layer ().end (); + } else { + m_polygons.get_layer ().replace (pw++, *p); + } + } + } + + m_polygons.get_layer ().erase (pw, m_polygons.get_layer ().end ()); + m_merged_polygons.clear (); + m_is_merged = merged_semantics (); + + return this; +} + +RegionDelegate *FlatRegion::merged_in_place () +{ + if (! m_is_merged) { + + if (m_merged_polygons_valid) { + + m_polygons.swap (m_merged_polygons); + m_merged_polygons.clear (); + m_is_merged = true; + return this; + + } else { + return merged_in_place (min_coherence (), 0); + } + + } else { + return this; + } +} + +RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int min_wc) +{ + if (empty ()) { + + // ignore empty + return new EmptyRegion (); + + } else if (is_box ()) { + + // take box only if min_wc == 0, otherwise clear + if (min_wc > 0) { + return new EmptyRegion (); + } + + } else { + + invalidate_cache (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (min_wc); + db::ShapeGenerator pc (m_polygons, true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + m_is_merged = true; + + } + + return this; +} + +RegionDelegate *FlatRegion::merged () const +{ + if (! m_is_merged) { + + if (m_merged_polygons_valid) { + return new FlatRegion (m_merged_polygons, true); + } else { + return AsIfFlatRegion::merged (min_coherence (), 0); + } + + } else { + return clone (); + } +} + +RegionDelegate *FlatRegion::add (const Region &other) const +{ + std::auto_ptr new_region (new FlatRegion (*this)); + new_region->invalidate_cache (); + new_region->set_is_merged (false); + + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + new_region->raw_polygons ().insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); + + } else { + + size_t n = new_region->raw_polygons ().size (); + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + new_region->raw_polygons ().reserve (db::Polygon::tag (), n); + + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + } + + return new_region.release (); +} + +RegionDelegate *FlatRegion::add_in_place (const Region &other) +{ + invalidate_cache (); + m_is_merged = false; + + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + m_polygons.insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); + + } else { + + size_t n = m_polygons.size (); + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + m_polygons.reserve (db::Polygon::tag (), n); + + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + m_polygons.insert (*p); + } + + } + + return this; +} + +const db::Polygon *FlatRegion::nth (size_t n) const +{ + return n < m_polygons.size () ? &m_polygons.get_layer ().begin () [n] : 0; +} + +bool FlatRegion::has_valid_polygons () const +{ + return true; +} + +bool FlatRegion::has_valid_merged_polygons () const +{ + return true; +} + +const db::RecursiveShapeIterator *FlatRegion::iter () const +{ + return 0; +} + +void +FlatRegion::insert (const db::Box &box) +{ + if (! box.empty () && box.width () > 0 && box.height () > 0) { + + if (empty ()) { + + m_polygons.insert (db::Polygon (box)); + m_is_merged = true; + update_bbox (box); + + } else { + + m_polygons.insert (db::Polygon (box)); + m_is_merged = false; + invalidate_cache (); + + } + + } +} + +void +FlatRegion::insert (const db::Path &path) +{ + if (path.points () > 0) { + m_polygons.insert (path.polygon ()); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::Polygon &polygon) +{ + if (polygon.holes () > 0 || polygon.vertices () > 0) { + m_polygons.insert (polygon); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::SimplePolygon &polygon) +{ + if (polygon.vertices () > 0) { + db::Polygon poly; + poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); + m_polygons.insert (poly); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::Shape &shape) +{ + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon poly; + shape.polygon (poly); + m_polygons.insert (poly); + m_is_merged = false; + invalidate_cache (); + } +} + +} + diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h new file mode 100644 index 000000000..72e712a6d --- /dev/null +++ b/src/db/db/dbFlatRegion.h @@ -0,0 +1,197 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbFlatRegion +#define HDR_dbFlatRegion + +#include "dbCommon.h" + +#include "dbAsIfFlatRegion.h" +#include "dbShapes.h" +#include "dbShapes2.h" + +namespace db { + +/** + * @brief An iterator delegate for the flat region + */ +class DB_PUBLIC FlatRegionIterator + : public RegionIteratorDelegate +{ +public: + typedef db::layer polygon_layer_type; + typedef polygon_layer_type::iterator iterator_type; + + FlatRegionIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // .. nothing yet .. + } + + virtual bool at_end () const + { + return m_from == m_to; + } + + virtual void increment () + { + ++m_from; + } + + virtual const value_type *get () const + { + return m_from.operator-> (); + } + + virtual RegionIteratorDelegate *clone () const + { + return new FlatRegionIterator (*this); + } + +private: + friend class Region; + + iterator_type m_from, m_to; +}; + +/** + * @brief A flat, polygon-set delegate + */ +class DB_PUBLIC FlatRegion + : public AsIfFlatRegion +{ +public: + typedef db::layer polygon_layer_type; + typedef polygon_layer_type::iterator polygon_iterator_type; + + FlatRegion (); + FlatRegion (const db::Shapes &polygons, bool is_merged); + FlatRegion (bool is_merged); + + FlatRegion (const FlatRegion &other); + + virtual ~FlatRegion (); + + RegionDelegate *clone () const + { + return new FlatRegion (*this); + } + + void reserve (size_t); + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + virtual bool is_merged () const; + + virtual RegionDelegate *merged_in_place (); + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); + virtual RegionDelegate *merged () const; + + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter); + + virtual RegionDelegate *add_in_place (const Region &other); + virtual RegionDelegate *add (const Region &other) const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + virtual bool has_valid_merged_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + void insert (const db::Box &box); + void insert (const db::Path &path); + void insert (const db::SimplePolygon &polygon); + void insert (const db::Polygon &polygon); + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon poly; + shape.polygon (poly); + poly.transform (trans); + insert (poly); + } + } + + template + void insert (const Iter &b, const Iter &e) + { + reserve (size () + (e - b)); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } + + template + void transform (const Trans &trans) + { + if (! trans.is_unity ()) { + for (polygon_iterator_type p = m_polygons.get_layer ().begin (); p != m_polygons.get_layer ().end (); ++p) { + m_polygons.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } + +protected: + virtual void merged_semantics_changed (); + virtual Box compute_bbox () const; + void invalidate_cache (); + void set_is_merged (bool m); + +private: + friend class AsIfFlatRegion; + + db::Shapes &raw_polygons () { return m_polygons; } + + FlatRegion &operator= (const FlatRegion &other); + + bool m_is_merged; + mutable db::Shapes m_polygons; + mutable db::Shapes m_merged_polygons; + mutable bool m_merged_polygons_valid; + + void init (); + void ensure_merged_polygons_valid () const; +}; + +} + +#endif + diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc new file mode 100644 index 000000000..7fbc11839 --- /dev/null +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -0,0 +1,274 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbOriginalLayerRegion.h" +#include "dbFlatRegion.h" +#include "dbRegion.h" +#include "dbShapeProcessor.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// OriginalLayerRegion implementation + +namespace +{ + + class OriginalLayerRegionIterator + : public RegionIteratorDelegate + { + public: + OriginalLayerRegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) + : m_rec_iter (iter), m_iter_trans (trans) + { + set (); + } + + virtual bool at_end () const + { + return m_rec_iter.at_end (); + } + + virtual void increment () + { + inc (); + set (); + } + + virtual const value_type *get () const + { + return &m_polygon; + } + + virtual RegionIteratorDelegate *clone () const + { + return new OriginalLayerRegionIterator (*this); + } + + private: + friend class Region; + + db::RecursiveShapeIterator m_rec_iter; + db::ICplxTrans m_iter_trans; + db::Polygon m_polygon; + + void set () + { + while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { + ++m_rec_iter; + } + if (! m_rec_iter.at_end ()) { + m_rec_iter.shape ().polygon (m_polygon); + m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); + } + } + + void inc () + { + if (! m_rec_iter.at_end ()) { + ++m_rec_iter; + } + } + }; + +} + +OriginalLayerRegion::OriginalLayerRegion () + : AsIfFlatRegion (), m_merged_polygons (false) +{ + init (); +} + +OriginalLayerRegion::OriginalLayerRegion (const OriginalLayerRegion &other) + : AsIfFlatRegion (other), + m_is_merged (other.m_is_merged), + m_merged_polygons (other.m_merged_polygons), + m_merged_polygons_valid (other.m_merged_polygons_valid), + m_iter (other.m_iter), + m_iter_trans (other.m_iter_trans) +{ + // .. nothing yet .. +} + +OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged) + : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si) +{ + init (); + + m_is_merged = is_merged; +} + +OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) + : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si), m_iter_trans (trans) +{ + init (); + + m_is_merged = is_merged; + set_merged_semantics (merged_semantics); +} + +OriginalLayerRegion::~OriginalLayerRegion () +{ + // .. nothing yet .. +} + +RegionDelegate * +OriginalLayerRegion::clone () const +{ + return new OriginalLayerRegion (*this); +} + +RegionIteratorDelegate * +OriginalLayerRegion::begin () const +{ + return new OriginalLayerRegionIterator (m_iter, m_iter_trans); +} + +RegionIteratorDelegate * +OriginalLayerRegion::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_polygons_valid (); + return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); + } +} + +std::pair +OriginalLayerRegion::begin_iter () const +{ + return std::make_pair (m_iter, m_iter_trans); +} + +std::pair +OriginalLayerRegion::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_polygons_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); + } +} + +bool +OriginalLayerRegion::empty () const +{ + return m_iter.at_end (); +} + +bool +OriginalLayerRegion::is_merged () const +{ + return m_is_merged; +} + +const db::Polygon * +OriginalLayerRegion::nth (size_t) const +{ + tl_assert (false); +} + +bool +OriginalLayerRegion::has_valid_polygons () const +{ + return false; +} + +bool +OriginalLayerRegion::has_valid_merged_polygons () const +{ + return merged_semantics () && ! m_is_merged; +} + +const db::RecursiveShapeIterator * +OriginalLayerRegion::iter () const +{ + return &m_iter; +} + +bool +OriginalLayerRegion::equals (const Region &other) const +{ + const OriginalLayerRegion *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return true; + } else { + return AsIfFlatRegion::equals (other); + } +} + +bool +OriginalLayerRegion::less (const Region &other) const +{ + const OriginalLayerRegion *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return false; + } else { + return AsIfFlatRegion::less (other); + } +} + +void +OriginalLayerRegion::init () +{ + m_is_merged = true; + m_merged_polygons_valid = false; +} + +void +OriginalLayerRegion::ensure_merged_polygons_valid () const +{ + if (! m_merged_polygons_valid) { + + m_merged_polygons.clear (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (0); + db::ShapeGenerator pc (m_merged_polygons); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + m_merged_polygons_valid = true; + + } +} + +} diff --git a/src/db/db/dbOriginalLayerRegion.h b/src/db/db/dbOriginalLayerRegion.h new file mode 100644 index 000000000..2584dda7b --- /dev/null +++ b/src/db/db/dbOriginalLayerRegion.h @@ -0,0 +1,83 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbOriginalLayerRegion +#define HDR_dbOriginalLayerRegion + +#include "dbCommon.h" + +#include "dbAsIfFlatRegion.h" + +namespace db { + +/** + * @brief An original layerregion based on a RecursiveShapeIterator + */ +class DB_PUBLIC OriginalLayerRegion + : public AsIfFlatRegion +{ +public: + OriginalLayerRegion (); + OriginalLayerRegion (const OriginalLayerRegion &other); + OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged = false); + OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); + virtual ~OriginalLayerRegion (); + + RegionDelegate *clone () const; + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + + virtual bool is_merged () const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + virtual bool has_valid_merged_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + +private: + OriginalLayerRegion &operator= (const OriginalLayerRegion &other); + + bool m_is_merged; + mutable db::Shapes m_merged_polygons; + mutable bool m_merged_polygons_valid; + mutable db::RecursiveShapeIterator m_iter; + db::ICplxTrans m_iter_trans; + + void init (); + void ensure_merged_polygons_valid () const; +}; + +} + +#endif + diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 439772415..9dfff7aca 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -22,2290 +22,13 @@ #include "dbRegion.h" -#include "dbLayoutUtils.h" -#include "dbEdgeProcessor.h" -#include "dbEdgePairRelations.h" -#include "dbEdges.h" -#include "dbEdgePairs.h" -#include "dbBoxConvert.h" -#include "dbBoxScanner.h" -#include "dbClip.h" -#include "dbPolygonTools.h" - -#include "tlVariant.h" - -#include -#include +#include "dbOriginalLayerRegion.h" +#include "dbEmptyRegion.h" +#include "dbFlatRegion.h" namespace db { -// ------------------------------------------------------------------------------------------------------------- - -RegionDelegate::RegionDelegate () -{ - m_report_progress = false; - m_merged_semantics = true; - m_strict_handling = false; - m_merge_min_coherence = false; -} - -RegionDelegate::RegionDelegate (const RegionDelegate &other) -{ - operator= (other); -} - -RegionDelegate & -RegionDelegate::operator= (const RegionDelegate &other) -{ - if (this != &other) { - m_report_progress = other.m_report_progress; - m_merged_semantics = other.m_merged_semantics; - m_strict_handling = other.m_strict_handling; - m_merge_min_coherence = other.m_merge_min_coherence; - } - return *this; -} - -RegionDelegate::~RegionDelegate () -{ - // .. nothing yet .. -} - -void RegionDelegate::enable_progress (const std::string &progress_desc) -{ - m_report_progress = true; - m_progress_desc = progress_desc; -} - -void RegionDelegate::disable_progress () -{ - m_report_progress = false; -} - -void RegionDelegate::set_min_coherence (bool f) -{ - m_merge_min_coherence = f; -} - -void RegionDelegate::set_merged_semantics (bool f) -{ - if (f != m_merged_semantics) { - m_merged_semantics = f; - merged_semantics_changed (); - } -} - -void RegionDelegate::set_strict_handling (bool f) -{ - m_strict_handling = f; -} - -// ------------------------------------------------------------------------------------------------------------- -// EmptyRegion implementation - -EmptyRegion::EmptyRegion () -{ - // .. nothing yet .. -} - -EmptyRegion::~EmptyRegion () -{ - // .. nothing yet .. -} - -EmptyRegion::EmptyRegion (const EmptyRegion &other) - : RegionDelegate (other) -{ - // .. nothing yet .. -} - -RegionDelegate * -EmptyRegion::clone () const -{ - return new EmptyRegion (*this); -} - -RegionDelegate * -EmptyRegion::add_in_place (const Region &other) -{ - return add (other); -} - -RegionDelegate * -EmptyRegion::add (const Region &other) const -{ - return other.delegate ()->clone (); -} - -RegionDelegate * -EmptyRegion::xor_with (const Region &other) const -{ - return or_with (other); -} - -RegionDelegate * -EmptyRegion::or_with (const Region &other) const -{ - if (other.empty ()) { - return new EmptyRegion (); - } else if (! other.strict_handling ()) { - return other.delegate ()->clone (); - } else { - return other.delegate ()->merged (); - } -} - -bool -EmptyRegion::equals (const Region &other) const -{ - return other.empty (); -} - -bool -EmptyRegion::less (const Region &other) const -{ - return other.empty () ? false : true; -} - -// ------------------------------------------------------------------------------------------------------------- -// AsIfFlagRegion implementation - -AsIfFlatRegion::AsIfFlatRegion () - : RegionDelegate (), m_bbox_valid (false) -{ - // .. nothing yet .. -} - -AsIfFlatRegion::~AsIfFlatRegion () -{ - // .. nothing yet .. -} - -std::string -AsIfFlatRegion::to_string (size_t nmax) const -{ - std::ostringstream os; - RegionIterator p (begin ()); - bool first = true; - for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { - if (! first) { - os << ";"; - } - first = false; - os << p->to_string (); - } - if (! p.at_end ()) { - os << "..."; - } - return os.str (); -} - -Edges -AsIfFlatRegion::edges (const EdgeFilterBase *filter) const -{ - Edges edges; - - size_t n = 0; - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - edges.reserve (n); - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - if (! filter || filter->selected (*e)) { - edges.insert (*e); - } - } - } - - return edges; -} - -RegionDelegate * -AsIfFlatRegion::hulls () const -{ - std::auto_ptr new_region (new FlatRegion (false)); - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - db::Polygon h; - h.assign_hull (p->begin_hull (), p->end_hull ()); - new_region->insert (h); - } - - return new_region.release (); -} - -RegionDelegate * -AsIfFlatRegion::holes () const -{ - std::auto_ptr new_region (new FlatRegion (false)); - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - for (size_t i = 0; i < p->holes (); ++i) { - db::Polygon h; - h.assign_hull (p->begin_hole ((unsigned int) i), p->end_hole ((unsigned int) i)); - new_region->insert (h); - } - } - - return new_region.release (); -} - -RegionDelegate * -AsIfFlatRegion::rounded_corners (double rinner, double router, unsigned int n) const -{ - std::auto_ptr new_region (new FlatRegion (false)); - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - new_region->insert (db::compute_rounded (*p, rinner, router, n)); - } - - return new_region.release (); -} - -RegionDelegate * -AsIfFlatRegion::smoothed (coord_type d) const -{ - std::auto_ptr new_region (new FlatRegion (false)); - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - new_region->insert (db::smooth (*p, d)); - } - - return new_region.release (); -} - -RegionDelegate * -AsIfFlatRegion::in (const Region &other, bool invert) const -{ - std::set op; - for (RegionIterator o (other.begin_merged ()); ! o.at_end (); ++o) { - op.insert (*o); - } - - std::auto_ptr new_region (new FlatRegion (false)); - - for (RegionIterator o (begin_merged ()); ! o.at_end (); ++o) { - if ((op.find (*o) == op.end ()) == invert) { - new_region->insert (*o); - } - } - - return new_region.release (); -} - -size_t -AsIfFlatRegion::size () const -{ - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - ++n; - } - return n; -} - -bool -AsIfFlatRegion::is_box () const -{ - RegionIterator p (begin ()); - if (p.at_end ()) { - return false; - } else { - const db::Polygon &poly = *p; - ++p; - if (! p.at_end ()) { - return false; - } else { - return poly.is_box (); - } - } -} - -AsIfFlatRegion::area_type -AsIfFlatRegion::area (const db::Box &box) const -{ - area_type a = 0; - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - if (box.empty () || p->box ().inside (box)) { - a += p->area (); - } else { - std::vector clipped; - clip_poly (*p, box, clipped); - for (std::vector::const_iterator c = clipped.begin (); c != clipped.end (); ++c) { - a += c->area (); - } - } - } - - return a; -} - -AsIfFlatRegion::perimeter_type -AsIfFlatRegion::perimeter (const db::Box &box) const -{ - perimeter_type d = 0; - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - - if (box.empty () || p->box ().inside (box)) { - d += p->perimeter (); - } else { - - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - - if (box.empty ()) { - d += (*e).length (); - } else { - - std::pair ce = (*e).clipped (box); - if (ce.first) { - - db::Coord dx = ce.second.dx (); - db::Coord dy = ce.second.dy (); - db::Coord x = ce.second.p1 ().x (); - db::Coord y = ce.second.p1 ().y (); - if ((dx == 0 && x == box.left () && dy < 0) || - (dx == 0 && x == box.right () && dy > 0) || - (dy == 0 && y == box.top () && dx < 0) || - (dy == 0 && y == box.bottom () && dx > 0)) { - // not counted -> box is at outside side of the edge - } else { - d += ce.second.length (); - } - - } - - } - - } - - } - - } - - return d; -} - -Box AsIfFlatRegion::bbox () const -{ - if (! m_bbox_valid) { - m_bbox = compute_bbox (); - m_bbox_valid = true; - } - return m_bbox; -} - -Box AsIfFlatRegion::compute_bbox () const -{ - db::Box b; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - b += p->box (); - } - return b; -} - -void AsIfFlatRegion::update_bbox (const db::Box &b) -{ - m_bbox = b; - m_bbox_valid = true; -} - -void AsIfFlatRegion::invalidate_bbox () -{ - m_bbox_valid = false; -} - -RegionDelegate * -AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const -{ - std::auto_ptr new_region (new FlatRegion ()); - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { - new_region->insert (*p); - } - } - - return new_region.release (); -} - -namespace -{ - -/** - * @brief A helper class for the region to edge interaction functionality - * - * Note: This special scanner uses pointers to two different objects: edges and polygons. - * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate - * pointers to edges. - * - * There is a special box converter which is able to sort that out as well. - */ -template -class region_to_edge_interaction_filter - : public db::box_scanner_receiver -{ -public: - region_to_edge_interaction_filter (OutputContainer &output) - : mp_output (&output), m_inverse (false) - { - // .. nothing yet .. - } - - region_to_edge_interaction_filter (OutputContainer &output, const db::RegionIterator &polygons) - : mp_output (&output), m_inverse (true) - { - for (db::RegionIterator p = polygons; ! p.at_end (); ++p) { - m_seen.insert (&*p); - } - } - - void add (const char *o1, size_t p1, const char *o2, size_t p2) - { - const db::Edge *e = 0; - const db::Polygon *p = 0; - - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - if (p1 == 0 && p2 == 1) { - e = reinterpret_cast (o1); - p = reinterpret_cast (o2 - 1); - } else if (p1 == 1 && p2 == 0) { - e = reinterpret_cast (o2); - p = reinterpret_cast (o1 - 1); - } - - if (e && p && (m_seen.find (p) == m_seen.end ()) != m_inverse) { - - // A polygon and an edge interact if the edge is either inside completely - // of at least one edge of the polygon intersects with the edge - bool interacts = false; - if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) { - interacts = true; - } else { - for (db::Polygon::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) { - if ((*pe).intersect (*e)) { - interacts = true; - } - } - } - - if (interacts) { - if (m_inverse) { - m_seen.erase (p); - } else { - m_seen.insert (p); - mp_output->insert (*p); - } - } - - } - } - - void fill_output () - { - for (std::set::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) { - mp_output->insert (**p); - } - } - -private: - OutputContainer *mp_output; - std::set m_seen; - bool m_inverse; -}; - -/** - * @brief A special box converter that splits the pointers into polygon and edge pointers - */ -struct EdgeOrRegionBoxConverter -{ - typedef db::Box box_type; - - db::Box operator() (const char &c) const - { - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - const char *cp = &c; - if ((size_t (cp) & 1) == 1) { - // it's a polygon - return (reinterpret_cast (cp - 1))->box (); - } else { - // it's an edge - const db::Edge *e = reinterpret_cast (cp); - return db::Box (e->p1 (), e->p2 ()); - } - } -}; - -} - -RegionDelegate * -AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse) const -{ - if (other.empty ()) { - if (! inverse) { - return new EmptyRegion (); - } else { - return clone (); - } - } else if (empty ()) { - return clone (); - } - - db::box_scanner scanner (report_progress (), progress_desc ()); - scanner.reserve (size () + other.size ()); - - AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ()); - - for ( ; ! p.at_end (); ++p) { - scanner.insert ((char *) p.operator-> () + 1, 1); - } - - other.ensure_valid_merged_edges (); - for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - std::auto_ptr output (new FlatRegion (false)); - EdgeOrRegionBoxConverter bc; - - if (! inverse) { - region_to_edge_interaction_filter filter (output->raw_polygons ()); - scanner.process (filter, 1, bc); - } else { - region_to_edge_interaction_filter filter (output->raw_polygons (), RegionIterator (begin_merged ())); - scanner.process (filter, 1, bc); - filter.fill_output (); - } - - return output.release (); -} - -RegionDelegate * -AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const -{ - db::EdgeProcessor ep (report_progress (), progress_desc ()); - - // shortcut - if (empty ()) { - return clone (); - } else if (other.empty ()) { - // clear, if b is empty and - // * mode is inside or interacting and inverse is false ("inside" or "interacting") - // * mode is outside and inverse is true ("not outside") - if ((mode <= 0) != inverse) { - return new EmptyRegion (); - } else { - return clone (); - } - } - - for (RegionIterator p = other.begin (); ! p.at_end (); ++p) { - if (p->box ().touches (bbox ())) { - ep.insert (*p, 0); - } - } - - size_t n = 1; - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) { - if (mode > 0 || p->box ().touches (other.bbox ())) { - ep.insert (*p, n); - } - } - - db::InteractionDetector id (mode, 0); - id.set_include_touching (touching); - db::EdgeSink es; - ep.process (es, id); - id.finish (); - - std::auto_ptr output (new FlatRegion (false)); - - n = 0; - std::set selected; - for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { - ++n; - selected.insert (i->second); - } - - output->reserve (n); - - n = 1; - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) { - if ((selected.find (n) == selected.end ()) == inverse) { - output->raw_polygons ().insert (*p); - } - } - - return output.release (); -} - -EdgePairs -AsIfFlatRegion::grid_check (db::Coord gx, db::Coord gy) const -{ - EdgePairs out; - - gx = std::max (db::Coord (1), gx); - gy = std::max (db::Coord (1), gy); - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - - for (size_t i = 0; i < p->holes () + 1; ++i) { - - db::Polygon::polygon_contour_iterator b, e; - - if (i == 0) { - b = p->begin_hull (); - e = p->end_hull (); - } else { - b = p->begin_hole ((unsigned int) (i - 1)); - e = p->end_hole ((unsigned int) (i - 1)); - } - - for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { - if (((*pt).x () % gx) != 0 || ((*pt).y () % gy) != 0) { - out.insert (EdgePair (db::Edge (*pt, *pt), db::Edge (*pt, *pt))); - } - } - - } - - } - - return out; -} - -static bool ac_less (double cos_a, bool gt180_a, double cos_b, bool gt180_b) -{ - if (gt180_a != gt180_b) { - return gt180_a < gt180_b; - } else { - if (gt180_a) { - return cos_a < cos_b - 1e-10; - } else { - return cos_a > cos_b + 1e-10; - } - } -} - -EdgePairs -AsIfFlatRegion::angle_check (double min, double max, bool inverse) const -{ - EdgePairs out; - - double cos_min = cos (std::max (0.0, std::min (360.0, min)) / 180.0 * M_PI); - double cos_max = cos (std::max (0.0, std::min (360.0, max)) / 180.0 * M_PI); - bool gt180_min = min > 180.0; - bool gt180_max = max > 180.0; - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - - for (size_t i = 0; i < p->holes () + 1; ++i) { - - const db::Polygon::contour_type *h = 0; - if (i == 0) { - h = &p->hull (); - } else { - h = &p->hole ((unsigned int) (i - 1)); - } - - size_t np = h->size (); - - for (size_t j = 0; j < np; ++j) { - - db::Edge e ((*h) [j], (*h) [(j + 1) % np]); - db::Edge ee (e.p2 (), (*h) [(j + 2) % np]); - double le = e.double_length (); - double lee = ee.double_length (); - - double cos_a = -db::sprod (e, ee) / (le * lee); - bool gt180_a = db::vprod_sign (e, ee) > 0; - - if ((ac_less (cos_a, gt180_a, cos_max, gt180_max) && !ac_less (cos_a, gt180_a, cos_min, gt180_min)) == !inverse) { - out.insert (EdgePair (e, ee)); - } - - } - - } - - } - - return out; -} - -static inline db::Coord snap_to_grid (db::Coord c, db::Coord g) -{ - // This form of snapping always snaps g/2 to right/top. - if (c < 0) { - c = -g * ((-c + (g - 1) / 2) / g); - } else { - c = g * ((c + g / 2) / g); - } - return c; -} - -RegionDelegate * -AsIfFlatRegion::snapped (db::Coord gx, db::Coord gy) -{ - std::auto_ptr new_region (new FlatRegion (merged_semantics ())); - - gx = std::max (db::Coord (1), gx); - gy = std::max (db::Coord (1), gy); - - std::vector pts; - - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - - db::Polygon pnew; - - for (size_t i = 0; i < p->holes () + 1; ++i) { - - pts.clear (); - - db::Polygon::polygon_contour_iterator b, e; - - if (i == 0) { - b = p->begin_hull (); - e = p->end_hull (); - } else { - b = p->begin_hole ((unsigned int) (i - 1)); - e = p->end_hole ((unsigned int) (i - 1)); - } - - for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { - pts.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); - } - - if (i == 0) { - pnew.assign_hull (pts.begin (), pts.end ()); - } else { - pnew.insert_hole (pts.begin (), pts.end ()); - } - - } - - new_region->raw_polygons ().insert (pnew); - - } - - return new_region.release (); -} - -namespace -{ - /** - * @brief A helper class to implement the strange polygon detector - */ - struct StrangePolygonInsideFunc - { - inline bool operator() (int wc) const - { - return wc < 0 || wc > 1; - } - }; -} - -RegionDelegate * -AsIfFlatRegion::strange_polygon_check () const -{ - EdgeProcessor ep; - std::auto_ptr new_region (new FlatRegion (merged_semantics ())); - - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - - ep.clear (); - ep.insert (*p); - - StrangePolygonInsideFunc inside; - db::GenericMerge op (inside); - db::ShapeGenerator pc (new_region->raw_polygons (), false); - db::PolygonGenerator pg (pc, false, false); - ep.process (pg, op); - } - - return new_region.release (); -} - - -namespace { - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - */ -class Edge2EdgeCheck - : public db::box_scanner_receiver -{ -public: - Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool different_polygons, bool requires_different_layers) - : mp_check (&check), mp_output (&output), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), - m_pass (0) - { - m_distance = check.distance (); - } - - bool prepare_next_pass () - { - ++m_pass; - - if (m_pass == 1) { - - if (! m_ep.empty ()) { - m_ep_discarded.resize (m_ep.size (), false); - return true; - } - - } else if (m_pass == 2) { - - std::vector::const_iterator d = m_ep_discarded.begin (); - std::vector::const_iterator ep = m_ep.begin (); - while (ep != m_ep.end ()) { - tl_assert (d != m_ep_discarded.end ()); - if (! *d) { - mp_output->insert (*ep); - } - ++d; - ++ep; - } - - } - - return false; - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - if (m_pass == 0) { - - // Overlap or inside checks require input from different layers - if ((! m_different_polygons || p1 != p2) && (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0)) { - - // ensure that the first check argument is of layer 1 and the second of - // layer 2 (unless both are of the same layer) - int l1 = int (p1 & size_t (1)); - int l2 = int (p2 & size_t (1)); - - db::EdgePair ep; - if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { - - // found a violation: store inside the local buffer for now. In the second - // pass we will eliminate those which are shielded completely. - size_t n = m_ep.size (); - m_ep.push_back (ep); - m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n)); - m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n)); - - } - - } - - } else { - - // a simple (complete) shielding implementation which is based on the - // assumption that shielding is relevant as soon as a foreign edge cuts through - // both of the edge pair's connecting edges. - - // TODO: this implementation does not take into account the nature of the - // EdgePair - because of "whole_edge" it may not reflect the part actually - // violating the distance. - - std::vector n1, n2; - - for (unsigned int p = 0; p < 2; ++p) { - - std::pair k (*o1, p1); - for (std::multimap, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) { - n1.push_back (i->second); - } - - std::sort (n1.begin (), n1.end ()); - - std::swap (o1, o2); - std::swap (p1, p2); - n1.swap (n2); - - } - - for (unsigned int p = 0; p < 2; ++p) { - - std::vector nn; - std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); - - for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { - if (! m_ep_discarded [*i]) { - db::EdgePair ep = m_ep [*i].normalized (); - if (db::Edge (ep.first ().p1 (), ep.second ().p2 ()).intersect (*o2) && - db::Edge (ep.second ().p1 (), ep.first ().p2 ()).intersect (*o2)) { - m_ep_discarded [*i] = true; - } - } - } - - std::swap (o1, o2); - std::swap (p1, p2); - n1.swap (n2); - - } - - } - - } - - /** - * @brief Gets a value indicating whether the check requires different layers - */ - bool requires_different_layers () const - { - return m_requires_different_layers; - } - - /** - * @brief Sets a value indicating whether the check requires different layers - */ - void set_requires_different_layers (bool f) - { - m_requires_different_layers = f; - } - - /** - * @brief Gets a value indicating whether the check requires different layers - */ - bool different_polygons () const - { - return m_different_polygons; - } - - /** - * @brief Sets a value indicating whether the check requires different layers - */ - void set_different_polygons (bool f) - { - m_different_polygons = f; - } - - /** - * @brief Gets the distance value - */ - EdgeRelationFilter::distance_type distance () const - { - return m_distance; - } - -private: - const EdgeRelationFilter *mp_check; - EdgePairs *mp_output; - bool m_requires_different_layers; - bool m_different_polygons; - EdgeRelationFilter::distance_type m_distance; - std::vector m_ep; - std::multimap, size_t> m_e2ep; - std::vector m_ep_discarded; - unsigned int m_pass; -}; - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - */ -class Poly2PolyCheck - : public db::box_scanner_receiver -{ -public: - Poly2PolyCheck (Edge2EdgeCheck &output) - : mp_output (&output) - { - // .. nothing yet .. - } - - void finish (const db::Polygon *o, size_t p) - { - enter (*o, p); - } - - void enter (const db::Polygon &o, size_t p) - { - if (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()) { - - // finally we check the polygons vs. itself for checks involving intra-polygon interactions - - m_scanner.clear (); - m_scanner.reserve (o.vertices ()); - - m_edges.clear (); - m_edges.reserve (o.vertices ()); - - for (db::Polygon::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p); - } - - tl_assert (m_edges.size () == o.vertices ()); - - m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); - - } - } - - void add (const db::Polygon *o1, size_t p1, const db::Polygon *o2, size_t p2) - { - enter (*o1, p1, *o2, p2); - } - - void enter (const db::Polygon &o1, size_t p1, const db::Polygon &o2, size_t p2) - { - if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { - - m_scanner.clear (); - m_scanner.reserve (o1.vertices () + o2.vertices ()); - - m_edges.clear (); - m_edges.reserve (o1.vertices () + o2.vertices ()); - - for (db::Polygon::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p1); - } - - for (db::Polygon::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p2); - } - - tl_assert (m_edges.size () == o1.vertices () + o2.vertices ()); - - // temporarily disable intra-polygon check in that step .. we do that later in finish() - // if required (#650). - bool no_intra = mp_output->different_polygons (); - mp_output->set_different_polygons (true); - - m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); - - mp_output->set_different_polygons (no_intra); - - } - } - -private: - db::box_scanner m_scanner; - Edge2EdgeCheck *mp_output; - std::vector m_edges; -}; - -} - -EdgePairs -AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const -{ - EdgePairs result; - - db::box_scanner scanner (report_progress (), progress_desc ()); - scanner.reserve (size () + (other ? other->size () : 0)); - - AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ()); - - size_t n = 0; - for ( ; ! p.at_end (); ++p) { - scanner.insert (p.operator-> (), n); - n += 2; - } - - AddressablePolygonDelivery po; - - if (other) { - - po = other->addressable_merged_polygons (); - - n = 1; - for ( ; ! po.at_end (); ++po) { - scanner.insert (po.operator-> (), n); - n += 2; - } - - } - - EdgeRelationFilter check (rel, d, metrics); - check.set_include_zero (other != 0); - check.set_whole_edges (whole_edges); - check.set_ignore_angle (ignore_angle); - check.set_min_projection (min_projection); - check.set_max_projection (max_projection); - - Edge2EdgeCheck edge_check (check, result, different_polygons, other != 0); - Poly2PolyCheck poly_check (edge_check); - - do { - scanner.process (poly_check, d, db::box_convert ()); - } while (edge_check.prepare_next_pass ()); - - return result; -} - -EdgePairs -AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const -{ - EdgePairs result; - - EdgeRelationFilter check (rel, d, metrics); - check.set_whole_edges (whole_edges); - check.set_ignore_angle (ignore_angle); - check.set_min_projection (min_projection); - check.set_max_projection (max_projection); - - Edge2EdgeCheck edge_check (check, result, false, false); - Poly2PolyCheck poly_check (edge_check); - - do { - - size_t n = 0; - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - poly_check.enter (*p, n); - n += 2; - } - - } while (edge_check.prepare_next_pass ()); - - return result; -} - -RegionDelegate * -AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc) const -{ - if (empty ()) { - - return new EmptyRegion (); - - } else if (is_box ()) { - - // take box only if min_wc == 0, otherwise clear - if (min_wc > 0) { - return new EmptyRegion (); - } else { - return clone (); - } - - } else { - - db::EdgeProcessor ep (report_progress (), progress_desc ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - std::auto_ptr new_region (new FlatRegion (true)); - - // and run the merge step - db::MergeOp op (min_wc); - db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); - ep.process (pg, op); - - return new_region.release (); - - } -} - -RegionDelegate * -AsIfFlatRegion::region_from_box (const db::Box &b) -{ - if (! b.empty () && b.width () > 0 && b.height () > 0) { - FlatRegion *new_region = new FlatRegion (); - new_region->insert (b); - return new_region; - } else { - return new EmptyRegion (); - } -} - -RegionDelegate * -AsIfFlatRegion::sized (coord_type d, unsigned int mode) const -{ - return sized (d, d, mode); -} - -RegionDelegate * -AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const -{ - if (empty ()) { - - // ignore empty - return new EmptyRegion (); - - } else if (is_box () && mode >= 2) { - - // simplified handling for a box - db::Box b = bbox ().enlarged (db::Vector (dx, dy)); - return region_from_box (b); - - } else if (! merged_semantics ()) { - - // Generic case - std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); - - db::ShapeGenerator pc (new_region->raw_polygons (), false); - db::PolygonGenerator pg (pc, false, true); - db::SizingPolygonFilter sf (pg, dx, dy, mode); - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - sf.put (*p); - } - - return new_region.release (); - - } else { - - // Generic case - the size operation will merge first - db::EdgeProcessor ep (report_progress (), progress_desc ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); - db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); - db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); - db::SizingPolygonFilter siz (pg2, dx, dy, mode); - db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/); - db::BooleanOp op (db::BooleanOp::Or); - ep.process (pg, op); - - return new_region.release (); - - } -} - -RegionDelegate * -AsIfFlatRegion::and_with (const Region &other) const -{ - if (empty () || other.empty ()) { - - // Nothing to do - return new EmptyRegion (); - - } else if (is_box () && other.is_box ()) { - - // Simplified handling for boxes - db::Box b = bbox (); - b &= other.bbox (); - return region_from_box (b); - - } else if (is_box () && ! other.strict_handling ()) { - - // map AND with box to clip .. - db::Box b = bbox (); - std::auto_ptr new_region (new FlatRegion (false)); - - std::vector clipped; - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - clipped.clear (); - clip_poly (*p, b, clipped); - new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); - } - - return new_region.release (); - - } else if (other.is_box () && ! strict_handling ()) { - - // map AND with box to clip .. - db::Box b = other.bbox (); - std::auto_ptr new_region (new FlatRegion (false)); - - std::vector clipped; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - clipped.clear (); - clip_poly (*p, b, clipped); - new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); - } - - return new_region.release (); - - } else if (! bbox ().overlaps (other.bbox ())) { - - // Result will be nothing - return new EmptyRegion (); - - } else { - - // Generic case - db::EdgeProcessor ep (report_progress (), progress_desc ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - std::auto_ptr new_region (new FlatRegion (true)); - db::BooleanOp op (db::BooleanOp::And); - db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); - ep.process (pg, op); - - return new_region.release (); - - } -} - -RegionDelegate * -AsIfFlatRegion::not_with (const Region &other) const -{ - if (empty ()) { - - // Nothing to do - return new EmptyRegion (); - - } else if (other.empty () && ! strict_handling ()) { - - // Nothing to do - return clone (); - - } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling ()) { - - // Nothing to do - return clone (); - - } else { - - // Generic case - db::EdgeProcessor ep (report_progress (), progress_desc ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - std::auto_ptr new_region (new FlatRegion (true)); - db::BooleanOp op (db::BooleanOp::ANotB); - db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); - ep.process (pg, op); - - return new_region.release (); - - } -} - -RegionDelegate * -AsIfFlatRegion::xor_with (const Region &other) const -{ - if (empty () && ! other.strict_handling ()) { - - return other.delegate ()->clone (); - - } else if (other.empty () && ! strict_handling ()) { - - return clone (); - - } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { - - // Simplified handling for disjunct case - return or_with (other); - - } else { - - // Generic case - db::EdgeProcessor ep (report_progress (), progress_desc ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - std::auto_ptr new_region (new FlatRegion (true)); - db::BooleanOp op (db::BooleanOp::Xor); - db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); - ep.process (pg, op); - - return new_region.release (); - - } -} - -RegionDelegate * -AsIfFlatRegion::or_with (const Region &other) const -{ - if (empty () && ! other.strict_handling ()) { - - return other.delegate ()->clone (); - - } else if (other.empty () && ! strict_handling ()) { - - // Nothing to do - return clone (); - - } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { - - // Simplified handling for disjunct case - return add (other); - - } else { - - // Generic case - db::EdgeProcessor ep (report_progress (), progress_desc ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - std::auto_ptr new_region (new FlatRegion (true)); - db::BooleanOp op (db::BooleanOp::Or); - db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); - ep.process (pg, op); - - return new_region.release (); - - } -} - -RegionDelegate * -AsIfFlatRegion::add (const Region &other) const -{ - FlatRegion *other_flat = dynamic_cast (other.delegate ()); - if (other_flat) { - - std::auto_ptr new_region (new FlatRegion (*other_flat)); - new_region->set_is_merged (false); - new_region->invalidate_cache (); - - size_t n = new_region->raw_polygons ().size () + size (); - - new_region->reserve (n); - - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - new_region->raw_polygons ().insert (*p); - } - - return new_region.release (); - - } else { - - std::auto_ptr new_region (new FlatRegion (false /*not merged*/)); - - size_t n = size () + other.size (); - - new_region->reserve (n); - - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - new_region->raw_polygons ().insert (*p); - } - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - new_region->raw_polygons ().insert (*p); - } - - return new_region.release (); - - } -} - -bool -AsIfFlatRegion::equals (const Region &other) const -{ - if (empty () != other.empty ()) { - return false; - } - if (size () != other.size ()) { - return false; - } - RegionIterator o1 (begin ()); - RegionIterator o2 (other.begin ()); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return false; - } - ++o1; - ++o2; - } - return true; -} - -bool -AsIfFlatRegion::less (const Region &other) const -{ - if (empty () != other.empty ()) { - return empty () < other.empty (); - } - if (size () != other.size ()) { - return (size () < other.size ()); - } - RegionIterator o1 (begin ()); - RegionIterator o2 (other.begin ()); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return *o1 < *o2; - } - ++o1; - ++o2; - } - return false; -} - -// ------------------------------------------------------------------------------------------------------------- -// FlatRegion implementation - -namespace -{ - - class FlatRegionIterator - : public RegionIteratorDelegate - { - public: - typedef db::layer polygon_layer_type; - typedef polygon_layer_type::iterator iterator_type; - - FlatRegionIterator (iterator_type from, iterator_type to) - : m_from (from), m_to (to) - { - // .. nothing yet .. - } - - virtual bool at_end () const - { - return m_from == m_to; - } - - virtual void increment () - { - ++m_from; - } - - virtual const value_type *get () const - { - return m_from.operator-> (); - } - - virtual RegionIteratorDelegate *clone () const - { - return new FlatRegionIterator (*this); - } - - private: - friend class Region; - - iterator_type m_from, m_to; - }; - -} - - -FlatRegion::FlatRegion () - : AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false) -{ - init (); -} - -FlatRegion::~FlatRegion () -{ - // .. nothing yet .. -} - -FlatRegion::FlatRegion (const FlatRegion &other) - : AsIfFlatRegion (other), m_polygons (false), m_merged_polygons (false) -{ - init (); - - m_is_merged = other.m_is_merged; - m_polygons = other.m_polygons; - m_merged_polygons = other.m_merged_polygons; - m_merged_polygons_valid = other.m_merged_polygons_valid; -} - -FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) - : AsIfFlatRegion (), m_polygons (polygons), m_merged_polygons (false) -{ - init (); - - m_is_merged = is_merged; -} - -FlatRegion::FlatRegion (bool is_merged) - : AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false) -{ - init (); - - m_is_merged = is_merged; -} - -void FlatRegion::set_is_merged (bool m) -{ - m_is_merged = m; -} - -void FlatRegion::invalidate_cache () -{ - invalidate_bbox (); - m_merged_polygons.clear (); - m_merged_polygons_valid = false; -} - -void FlatRegion::init () -{ - m_is_merged = true; - m_merged_polygons_valid = false; -} - -void FlatRegion::merged_semantics_changed () -{ - m_merged_polygons.clear (); - m_merged_polygons_valid = false; -} - -void FlatRegion::reserve (size_t n) -{ - m_polygons.reserve (db::Polygon::tag (), n); -} - -void -FlatRegion::ensure_merged_polygons_valid () const -{ - if (! m_merged_polygons_valid) { - - m_merged_polygons.clear (); - - db::EdgeProcessor ep (report_progress (), progress_desc ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (0); - db::ShapeGenerator pc (m_merged_polygons); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); - ep.process (pg, op); - - m_merged_polygons_valid = true; - - } -} - -RegionIteratorDelegate *FlatRegion::begin () const -{ - return new FlatRegionIterator (m_polygons.get_layer ().begin (), m_polygons.get_layer ().end ()); -} - -RegionIteratorDelegate *FlatRegion::begin_merged () const -{ - if (! merged_semantics () || m_is_merged) { - return begin (); - } else { - ensure_merged_polygons_valid (); - return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); - } -} - -std::pair FlatRegion::begin_iter () const -{ - return std::make_pair (db::RecursiveShapeIterator (m_polygons), db::ICplxTrans ()); -} - -std::pair FlatRegion::begin_merged_iter () const -{ - if (! merged_semantics () || m_is_merged) { - return begin_iter (); - } else { - ensure_merged_polygons_valid (); - return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); - } -} - -bool FlatRegion::empty () const -{ - return m_polygons.empty (); -} - -size_t FlatRegion::size () const -{ - return m_polygons.size (); -} - -bool FlatRegion::is_merged () const -{ - return m_is_merged; -} - -Box FlatRegion::compute_bbox () const -{ - m_polygons.update_bbox (); - return m_polygons.bbox (); -} - -RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter) -{ - polygon_iterator_type pw = m_polygons.get_layer ().begin (); - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { - if (pw == m_polygons.get_layer ().end ()) { - m_polygons.get_layer ().insert (*p); - pw = m_polygons.get_layer ().end (); - } else { - m_polygons.get_layer ().replace (pw++, *p); - } - } - } - - m_polygons.get_layer ().erase (pw, m_polygons.get_layer ().end ()); - m_merged_polygons.clear (); - m_is_merged = merged_semantics (); - - return this; -} - -RegionDelegate *FlatRegion::merged_in_place () -{ - if (! m_is_merged) { - - if (m_merged_polygons_valid) { - - m_polygons.swap (m_merged_polygons); - m_merged_polygons.clear (); - m_is_merged = true; - return this; - - } else { - return merged_in_place (min_coherence (), 0); - } - - } else { - return this; - } -} - -RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int min_wc) -{ - if (empty ()) { - - // ignore empty - return new EmptyRegion (); - - } else if (is_box ()) { - - // take box only if min_wc == 0, otherwise clear - if (min_wc > 0) { - return new EmptyRegion (); - } - - } else { - - invalidate_cache (); - - db::EdgeProcessor ep (report_progress (), progress_desc ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (min_wc); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); - ep.process (pg, op); - - m_is_merged = true; - - } - - return this; -} - -RegionDelegate *FlatRegion::merged () const -{ - if (! m_is_merged) { - - if (m_merged_polygons_valid) { - return new FlatRegion (m_merged_polygons, true); - } else { - return AsIfFlatRegion::merged (min_coherence (), 0); - } - - } else { - return clone (); - } -} - -RegionDelegate *FlatRegion::add (const Region &other) const -{ - std::auto_ptr new_region (new FlatRegion (*this)); - new_region->invalidate_cache (); - new_region->set_is_merged (false); - - FlatRegion *other_flat = dynamic_cast (other.delegate ()); - if (other_flat) { - - new_region->raw_polygons ().insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); - - } else { - - size_t n = new_region->raw_polygons ().size (); - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - ++n; - } - - new_region->raw_polygons ().reserve (db::Polygon::tag (), n); - - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - new_region->raw_polygons ().insert (*p); - } - - } - - return new_region.release (); -} - -RegionDelegate *FlatRegion::add_in_place (const Region &other) -{ - invalidate_cache (); - m_is_merged = false; - - FlatRegion *other_flat = dynamic_cast (other.delegate ()); - if (other_flat) { - - m_polygons.insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); - - } else { - - size_t n = m_polygons.size (); - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - ++n; - } - - m_polygons.reserve (db::Polygon::tag (), n); - - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - m_polygons.insert (*p); - } - - } - - return this; -} - -const db::Polygon *FlatRegion::nth (size_t n) const -{ - return n < m_polygons.size () ? &m_polygons.get_layer ().begin () [n] : 0; -} - -bool FlatRegion::has_valid_polygons () const -{ - return true; -} - -bool FlatRegion::has_valid_merged_polygons () const -{ - return true; -} - -const db::RecursiveShapeIterator *FlatRegion::iter () const -{ - return 0; -} - -void -FlatRegion::insert (const db::Box &box) -{ - if (! box.empty () && box.width () > 0 && box.height () > 0) { - - if (empty ()) { - - m_polygons.insert (db::Polygon (box)); - m_is_merged = true; - update_bbox (box); - - } else { - - m_polygons.insert (db::Polygon (box)); - m_is_merged = false; - invalidate_cache (); - - } - - } -} - -void -FlatRegion::insert (const db::Path &path) -{ - if (path.points () > 0) { - m_polygons.insert (path.polygon ()); - m_is_merged = false; - invalidate_cache (); - } -} - -void -FlatRegion::insert (const db::Polygon &polygon) -{ - if (polygon.holes () > 0 || polygon.vertices () > 0) { - m_polygons.insert (polygon); - m_is_merged = false; - invalidate_cache (); - } -} - -void -FlatRegion::insert (const db::SimplePolygon &polygon) -{ - if (polygon.vertices () > 0) { - db::Polygon poly; - poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); - m_polygons.insert (poly); - m_is_merged = false; - invalidate_cache (); - } -} - -void -FlatRegion::insert (const db::Shape &shape) -{ - if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - db::Polygon poly; - shape.polygon (poly); - m_polygons.insert (poly); - m_is_merged = false; - invalidate_cache (); - } -} - -// ------------------------------------------------------------------------------------------------------------- -// OriginalLayerRegion implementation - -namespace -{ - - class OriginalLayerRegionIterator - : public RegionIteratorDelegate - { - public: - OriginalLayerRegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans) - { - set (); - } - - virtual bool at_end () const - { - return m_rec_iter.at_end (); - } - - virtual void increment () - { - inc (); - set (); - } - - virtual const value_type *get () const - { - return &m_polygon; - } - - virtual RegionIteratorDelegate *clone () const - { - return new OriginalLayerRegionIterator (*this); - } - - private: - friend class Region; - - db::RecursiveShapeIterator m_rec_iter; - db::ICplxTrans m_iter_trans; - db::Polygon m_polygon; - - void set () - { - while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { - ++m_rec_iter; - } - if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().polygon (m_polygon); - m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); - } - } - - void inc () - { - if (! m_rec_iter.at_end ()) { - ++m_rec_iter; - } - } - }; - -} - -OriginalLayerRegion::OriginalLayerRegion () - : AsIfFlatRegion (), m_merged_polygons (false) -{ - init (); -} - -OriginalLayerRegion::OriginalLayerRegion (const OriginalLayerRegion &other) - : AsIfFlatRegion (other), - m_is_merged (other.m_is_merged), - m_merged_polygons (other.m_merged_polygons), - m_merged_polygons_valid (other.m_merged_polygons_valid), - m_iter (other.m_iter), - m_iter_trans (other.m_iter_trans) -{ - // .. nothing yet .. -} - -OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged) - : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si) -{ - init (); - - m_is_merged = is_merged; -} - -OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) - : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si), m_iter_trans (trans) -{ - init (); - - m_is_merged = is_merged; - set_merged_semantics (merged_semantics); -} - -OriginalLayerRegion::~OriginalLayerRegion () -{ - // .. nothing yet .. -} - -RegionDelegate * -OriginalLayerRegion::clone () const -{ - return new OriginalLayerRegion (*this); -} - -RegionIteratorDelegate * -OriginalLayerRegion::begin () const -{ - return new OriginalLayerRegionIterator (m_iter, m_iter_trans); -} - -RegionIteratorDelegate * -OriginalLayerRegion::begin_merged () const -{ - if (! merged_semantics () || m_is_merged) { - return begin (); - } else { - ensure_merged_polygons_valid (); - return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); - } -} - -std::pair -OriginalLayerRegion::begin_iter () const -{ - return std::make_pair (m_iter, m_iter_trans); -} - -std::pair -OriginalLayerRegion::begin_merged_iter () const -{ - if (! merged_semantics () || m_is_merged) { - return begin_iter (); - } else { - ensure_merged_polygons_valid (); - return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); - } -} - -bool -OriginalLayerRegion::empty () const -{ - return m_iter.at_end (); -} - -bool -OriginalLayerRegion::is_merged () const -{ - return m_is_merged; -} - -const db::Polygon * -OriginalLayerRegion::nth (size_t) const -{ - tl_assert (false); -} - -bool -OriginalLayerRegion::has_valid_polygons () const -{ - return false; -} - -bool -OriginalLayerRegion::has_valid_merged_polygons () const -{ - return merged_semantics () && ! m_is_merged; -} - -const db::RecursiveShapeIterator * -OriginalLayerRegion::iter () const -{ - return &m_iter; -} - -bool -OriginalLayerRegion::equals (const Region &other) const -{ - const OriginalLayerRegion *other_delegate = dynamic_cast (other.delegate ()); - if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { - return true; - } else { - return AsIfFlatRegion::equals (other); - } -} - -bool -OriginalLayerRegion::less (const Region &other) const -{ - const OriginalLayerRegion *other_delegate = dynamic_cast (other.delegate ()); - if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { - return false; - } else { - return AsIfFlatRegion::less (other); - } -} - -void -OriginalLayerRegion::init () -{ - m_is_merged = true; - m_merged_polygons_valid = false; -} - -void -OriginalLayerRegion::ensure_merged_polygons_valid () const -{ - if (! m_merged_polygons_valid) { - - m_merged_polygons.clear (); - - db::EdgeProcessor ep (report_progress (), progress_desc ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (0); - db::ShapeGenerator pc (m_merged_polygons); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); - ep.process (pg, op); - - m_merged_polygons_valid = true; - - } -} - // ------------------------------------------------------------------------------------------------------------- // Region implementation @@ -2368,6 +91,54 @@ Region::set_delegate (RegionDelegate *delegate) } } +void +Region::clear () +{ + set_delegate (new EmptyRegion ()); +} + +void +Region::reserve (size_t n) +{ + flat_region ()->reserve (n); +} + +template +Region &Region::transform (const T &trans) +{ + flat_region ()->transform (trans); + return *this; +} + +// explicit instantiations +template <> Region &Region::transform (const db::ICplxTrans &); +template <> Region &Region::transform (const db::Trans &); + +template +void Region::insert (const Sh &shape) +{ + flat_region ()->insert (shape); +} + +template void Region::insert (const db::Box &); +template void Region::insert (const db::SimplePolygon &); +template void Region::insert (const db::Polygon &); +template void Region::insert (const db::Path &); + +void Region::insert (const db::Shape &shape) +{ + flat_region ()->insert (shape); +} + +template +void Region::insert (const db::Shape &shape, const T &trans) +{ + flat_region ()->insert (shape, trans); +} + +template void Region::insert (const db::Shape &, const db::ICplxTrans &); +template void Region::insert (const db::Shape &, const db::Trans &); + FlatRegion * Region::flat_region () { @@ -2382,1584 +153,6 @@ Region::flat_region () return region; } -// ------------------------------------------------------------------------------------------------------------- - -#if 0 -@@@@ original - -Region::Region (const RecursiveShapeIterator &si) - : m_polygons (false), m_merged_polygons (false), m_iter (si) -{ - init (); - // Make sure we restart the iterator and late-initialize it (this makes sure - // it refers to the configuration present then) - m_iter.reset (); - m_bbox_valid = false; - m_is_merged = false; -} - -Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics) - : m_polygons (false), m_merged_polygons (false), m_iter (si), m_iter_trans (trans) -{ - init (); - // Make sure we restart the iterator and late-initialize it (this makes sure - // it refers to the configuration present then) - m_iter.reset (); - m_bbox_valid = false; - m_is_merged = false; - m_merged_semantics = merged_semantics; -} - -bool -Region::operator== (const db::Region &other) const -{ - if (empty () != other.empty ()) { - return false; - } - if (size () != other.size ()) { - return false; - } - db::Region::const_iterator o1 = begin (); - db::Region::const_iterator o2 = other.begin (); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return false; - } - ++o1; - ++o2; - } - return true; -} - -bool -Region::operator< (const db::Region &other) const -{ - if (empty () != other.empty ()) { - return empty () < other.empty (); - } - if (size () != other.size ()) { - return (size () < other.size ()); - } - db::Region::const_iterator o1 = begin (); - db::Region::const_iterator o2 = other.begin (); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return *o1 < *o2; - } - ++o1; - ++o2; - } - return false; -} - -size_t -Region::size () const -{ - if (! has_valid_polygons ()) { - // If we have an iterator, we have to do it the hard way .. - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - ++n; - } - return n; - } else { - return m_polygons.size (); - } -} - -void -Region::set_strict_handling (bool f) -{ - m_strict_handling = f; -} - -void -Region::set_merged_semantics (bool f) -{ - if (f != m_merged_semantics) { - m_merged_semantics = f; - m_merged_polygons.clear (); - m_merged_polygons_valid = false; - } -} - -std::string -Region::to_string (size_t nmax) const -{ - std::ostringstream os; - const_iterator p = begin (); - bool first = true; - for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { - if (! first) { - os << ";"; - } - first = false; - os << p->to_string (); - } - if (! p.at_end ()) { - os << "..."; - } - return os.str (); -} - -Region::area_type -Region::area (const db::Box &box) const -{ - area_type a = 0; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - if (box.empty () || p->box ().inside (box)) { - a += p->area (); - } else { - std::vector clipped; - clip_poly (*p, box, clipped); - for (std::vector::const_iterator c = clipped.begin (); c != clipped.end (); ++c) { - a += c->area (); - } - } - } - - return a; -} - -Region::perimeter_type -Region::perimeter (const db::Box &box) const -{ - perimeter_type d = 0; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - - if (box.empty () || p->box ().inside (box)) { - d += p->perimeter (); - } else { - - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - - if (box.empty ()) { - d += (*e).length (); - } else { - - std::pair ce = (*e).clipped (box); - if (ce.first) { - - db::Coord dx = ce.second.dx (); - db::Coord dy = ce.second.dy (); - db::Coord x = ce.second.p1 ().x (); - db::Coord y = ce.second.p1 ().y (); - if ((dx == 0 && x == box.left () && dy < 0) || - (dx == 0 && x == box.right () && dy > 0) || - (dy == 0 && y == box.top () && dx < 0) || - (dy == 0 && y == box.bottom () && dx > 0)) { - // not counted -> box is at outside side of the edge - } else { - d += ce.second.length (); - } - - } - - } - - } - - } - - } - - return d; -} - -Region -Region::hulls () const -{ - Region region; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - db::Polygon h; - h.assign_hull (p->begin_hull (), p->end_hull ()); - region.insert (h); - } - - return region; -} - -Region -Region::holes () const -{ - Region region; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - for (size_t i = 0; i < p->holes (); ++i) { - db::Polygon h; - h.assign_hull (p->begin_hole ((unsigned int) i), p->end_hole ((unsigned int) i)); - region.insert (h); - } - } - - return region; -} - -Region -Region::rounded_corners (double rinner, double router, unsigned int n) const -{ - Region r; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - r.insert (db::compute_rounded (*p, rinner, router, n)); - } - return r; -} - -Region -Region::smoothed (coord_type d) const -{ - Region r; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - r.insert (db::smooth (*p, d)); - } - return r; -} - -Region -Region::in (const Region &other, bool invert) const -{ - std::set op; - for (const_iterator o = other.begin_merged (); ! o.at_end (); ++o) { - op.insert (*o); - } - - Region r; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if ((op.find (*o) == op.end ()) == invert) { - r.insert (*o); - } - } - - return r; -} - -Edges -Region::edges () const -{ - Edges edges; - - size_t n = 0; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - n += p->vertices (); - } - edges.reserve (n); - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - edges.insert (*e); - } - } - - return edges; -} - -bool -Region::is_box () const -{ - const_iterator p = begin (); - if (p.at_end ()) { - return false; - } else { - const db::Polygon &poly = *p; - ++p; - if (! p.at_end ()) { - return false; - } else { - return poly.is_box (); - } - } -} - -void -Region::swap (Region &other) -{ - std::swap (m_is_merged, other.m_is_merged); - std::swap (m_merged_semantics, other.m_merged_semantics); - std::swap (m_strict_handling, other.m_strict_handling); - std::swap (m_merge_min_coherence, other.m_merge_min_coherence); - m_polygons.swap (other.m_polygons); - m_merged_polygons.swap (other.m_merged_polygons); - std::swap (m_bbox, other.m_bbox); - std::swap (m_bbox_valid, other.m_bbox_valid); - std::swap (m_merged_polygons_valid, other.m_merged_polygons_valid); - std::swap (m_iter, other.m_iter); - std::swap (m_iter_trans, other.m_iter_trans); -} - -Region & -Region::merge () -{ - if (! m_is_merged) { - - if (m_merged_polygons_valid) { - - m_polygons.swap (m_merged_polygons); - m_merged_polygons.clear (); - m_is_merged = true; - - } else { - - merge (m_merge_min_coherence, 0); - - } - - } - - return *this; -} - -Region & -Region::merge (bool min_coherence, unsigned int min_wc) -{ - if (empty ()) { - - // ignore empty - - } else if (is_box ()) { - - // take box only if min_wc == 0, otherwise clear - if (min_wc > 0) { - clear (); - } - - } else { - - invalidate_cache (); - - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (min_wc); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = true; - - } - - return *this; -} - -Region & -Region::size (Region::coord_type dx, Region::coord_type dy, unsigned int mode) -{ - if (empty ()) { - - // ignore empty - - } else if (is_box () && mode >= 2) { - - // simplified handling for a box - db::Box b = bbox ().enlarged (db::Vector (dx, dy)); - m_polygons.clear (); - if (! b.empty () && b.width () > 0 && b.height () > 0) { - m_polygons.insert (db::Polygon (b)); - } else { - b = db::Box (); - } - - m_is_merged = true; - m_bbox = b; - m_bbox_valid = true; - - m_merged_polygons.clear (); - m_merged_polygons_valid = false; - set_valid_polygons (); - - } else if (! m_merged_semantics) { - - invalidate_cache (); - - // Generic case - db::Shapes output (false); - - db::ShapeGenerator pc (output, false); - db::PolygonGenerator pg (pc, false, true); - db::SizingPolygonFilter sf (pg, dx, dy, mode); - for (const_iterator p = begin (); ! p.at_end (); ++p) { - sf.put (*p); - } - - output.swap (m_polygons); - set_valid_polygons (); - - m_is_merged = false; - - } else { - - invalidate_cache (); - - // Generic case - the size operation will merge first - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); - db::SizingPolygonFilter siz (pg2, dx, dy, mode); - db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/); - db::BooleanOp op (db::BooleanOp::Or); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = false; - - } - - return *this; -} - -Region & -Region::operator&= (const Region &other) -{ - if (empty ()) { - - // Nothing to do - - } else if (other.empty ()) { - - clear (); - - } else if (is_box () && other.is_box ()) { - - // Simplified handling for boxes - db::Box b = bbox (); - b &= other.bbox (); - m_polygons.clear (); - if (! b.empty () && b.width () > 0 && b.height () > 0 ) { - m_polygons.insert (db::Polygon (b)); - } - - m_is_merged = true; - m_bbox = b; - m_bbox_valid = true; - - m_merged_polygons.clear (); - m_merged_polygons_valid = false; - set_valid_polygons (); - - } else if (is_box () && ! other.strict_handling ()) { - - // map AND with box to clip .. - db::Box b = bbox (); - m_polygons.clear (); - - std::vector clipped; - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - clipped.clear (); - clip_poly (*p, b, clipped); - m_polygons.insert (clipped.begin (), clipped.end ()); - } - - m_is_merged = false; - invalidate_cache (); - set_valid_polygons (); - - } else if (other.is_box () && ! m_strict_handling) { - - // map AND with box to clip .. - db::Box b = other.bbox (); - db::Shapes polygons (false); - - std::vector clipped; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - clipped.clear (); - clip_poly (*p, b, clipped); - polygons.insert (clipped.begin (), clipped.end ()); - } - - m_polygons.swap (polygons); - m_is_merged = false; - invalidate_cache (); - set_valid_polygons (); - - } else if (! bbox ().overlaps (other.bbox ())) { - - // Result will be nothing - clear (); - - } else { - - invalidate_cache (); - - // Generic case - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (const_iterator p = other.begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - db::BooleanOp op (db::BooleanOp::And); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_merge_min_coherence); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = true; - - } - - return *this; -} - -Region & -Region::operator-= (const Region &other) -{ - if (empty ()) { - - // Nothing to do - - } else if (other.empty () && ! m_strict_handling) { - - // Nothing to do - - } else if (! bbox ().overlaps (other.bbox ()) && ! m_strict_handling) { - - // Nothing to do - - } else { - - invalidate_cache (); - - // Generic case - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (const_iterator p = other.begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - db::BooleanOp op (db::BooleanOp::ANotB); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_merge_min_coherence); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = true; - - } - - return *this; -} - -Region & -Region::operator^= (const Region &other) -{ - if (empty () && ! other.strict_handling ()) { - - *this = other; - - } else if (other.empty () && ! m_strict_handling) { - - // Nothing to do - - } else if (! bbox ().overlaps (other.bbox ()) && ! m_strict_handling && ! other.strict_handling ()) { - - // Simplified handling for disjunct case - *this |= other; - - } else { - - invalidate_cache (); - - // Generic case - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (const_iterator p = other.begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - db::BooleanOp op (db::BooleanOp::Xor); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_merge_min_coherence); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = true; - - } - - return *this; -} - -Region & -Region::operator|= (const Region &other) -{ - if (empty () && ! other.strict_handling ()) { - - *this = other; - - } else if (other.empty () && ! m_strict_handling) { - - // Nothing to do - - } else if (! bbox ().overlaps (other.bbox ()) && ! m_strict_handling && ! other.strict_handling ()) { - - // Simplified handling for disjunct case - *this += other; - - } else { - - invalidate_cache (); - - // Generic case - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (const_iterator p = other.begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - db::BooleanOp op (db::BooleanOp::Or); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_merge_min_coherence); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = true; - - } - - return *this; -} - -Region & -Region::operator+= (const Region &other) -{ - invalidate_cache (); - - if (! has_valid_polygons ()) { - - m_polygons.clear (); - - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - ++n; - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - ++n; - } - - m_polygons.reserve (db::Polygon::tag (), n); - - for (const_iterator p = begin (); ! p.at_end (); ++p) { - m_polygons.insert (*p); - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - m_polygons.insert (*p); - } - - set_valid_polygons (); - - } else if (! other.has_valid_polygons ()) { - - size_t n = m_polygons.size (); - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - ++n; - } - - m_polygons.reserve (db::Polygon::tag (), n); - - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - m_polygons.insert (*p); - } - - } else { - m_polygons.insert (other.m_polygons.get_layer ().begin (), other.m_polygons.get_layer ().end ()); - } - - m_is_merged = false; - return *this; -} - -Region -Region::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const -{ - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // shortcut - if (empty ()) { - return *this; - } else if (other.empty ()) { - // clear, if b is empty and - // * mode is inside or interacting and inverse is false ("inside" or "interacting") - // * mode is outside and inverse is true ("not outside") - if ((mode <= 0) != inverse) { - return Region (); - } else { - return *this; - } - } - - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - if (p->box ().touches (bbox ())) { - ep.insert (*p, 0); - } - } - - size_t n = 1; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p, ++n) { - if (mode > 0 || p->box ().touches (other.bbox ())) { - ep.insert (*p, n); - } - } - - db::InteractionDetector id (mode, 0); - id.set_include_touching (touching); - db::EdgeSink es; - ep.process (es, id); - id.finish (); - - Region out; - n = 0; - std::set selected; - for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { - ++n; - selected.insert (i->second); - } - - out.reserve (n); - - n = 1; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p, ++n) { - if ((selected.find (n) == selected.end ()) == inverse) { - out.insert (*p); - } - } - - return out; -} - -void -Region::select_interacting_generic (const Region &other, int mode, bool touching, bool inverse) -{ - // shortcut - if (empty ()) { - return; - } else if (other.empty ()) { - // clear, if b is empty and - // * mode is inside or interacting and inverse is false ("inside" or "interacting") - // * mode is outside and inverse is true ("not outside") - if ((mode <= 0) != inverse) { - clear (); - } - return; - } - - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - if (p->box ().touches (bbox ())) { - ep.insert (*p, 0); - } - } - - size_t n = 1; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p, ++n) { - if (mode > 0 || p->box ().touches (other.bbox ())) { - ep.insert (*p, n); - } - } - - invalidate_cache (); - - db::InteractionDetector id (mode, 0); - id.set_include_touching (touching); - db::EdgeSink es; - ep.process (es, id); - id.finish (); - - db::Shapes out (false); - std::set selected; - n = 0; - for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { - selected.insert (i->second); - ++n; - } - - out.reserve (db::Polygon::tag (), n); - n = 1; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p, ++n) { - if ((selected.find (n) == selected.end ()) == inverse) { - out.insert (*p); - } - } - - m_polygons.swap (out); - set_valid_polygons (); -} - -namespace -{ - -/** - * @brief A helper class for the region to edge interaction functionality - * - * Note: This special scanner uses pointers to two different objects: edges and polygons. - * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate - * pointers to edges. - * - * There is a special box converter which is able to sort that out as well. - */ -template -class region_to_edge_interaction_filter - : public db::box_scanner_receiver -{ -public: - region_to_edge_interaction_filter (OutputContainer &output) - : mp_output (&output), m_inverse (false) - { - // .. nothing yet .. - } - - region_to_edge_interaction_filter (OutputContainer &output, const db::Region ®ion) - : mp_output (&output), m_inverse (true) - { - for (db::Region::const_iterator p = region.begin_merged (); ! p.at_end (); ++p) { - m_seen.insert (&*p); - } - } - - void add (const char *o1, size_t p1, const char *o2, size_t p2) - { - const db::Edge *e = 0; - const db::Polygon *p = 0; - - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - if (p1 == 0 && p2 == 1) { - e = reinterpret_cast (o1); - p = reinterpret_cast (o2 - 1); - } else if (p1 == 1 && p2 == 0) { - e = reinterpret_cast (o2); - p = reinterpret_cast (o1 - 1); - } - - if (e && p && (m_seen.find (p) == m_seen.end ()) != m_inverse) { - - // A polygon and an edge interact if the edge is either inside completely - // of at least one edge of the polygon intersects with the edge - bool interacts = false; - if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) { - interacts = true; - } else { - for (db::Polygon::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) { - if ((*pe).intersect (*e)) { - interacts = true; - } - } - } - - if (interacts) { - if (m_inverse) { - m_seen.erase (p); - } else { - m_seen.insert (p); - mp_output->insert (*p); - } - } - - } - } - - void fill_output () - { - for (std::set::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) { - mp_output->insert (**p); - } - } - -private: - OutputContainer *mp_output; - std::set m_seen; - bool m_inverse; -}; - -/** - * @brief A special box converter that splits the pointers into polygon and edge pointers - */ -struct EdgeOrRegionBoxConverter -{ - typedef db::Box box_type; - - db::Box operator() (const char &c) const - { - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - const char *cp = &c; - if ((size_t (cp) & 1) == 1) { - // it's a polygon - return (reinterpret_cast (cp - 1))->box (); - } else { - // it's an edge - const db::Edge *e = reinterpret_cast (cp); - return db::Box (e->p1 (), e->p2 ()); - } - } -}; - -} - -Region -Region::selected_interacting_generic (const Edges &other, bool inverse) const -{ - if (other.empty ()) { - if (! inverse) { - return Region (); - } else { - return *this; - } - } else if (empty ()) { - return *this; - } - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - scanner.insert ((char *) &*p + 1, 1); - } - - other.ensure_valid_merged_edges (); - for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - Region output; - EdgeOrRegionBoxConverter bc; - - if (! inverse) { - region_to_edge_interaction_filter filter (output); - scanner.process (filter, 1, bc); - } else { - region_to_edge_interaction_filter filter (output, *this); - scanner.process (filter, 1, bc); - filter.fill_output (); - } - - return output; -} - -void -Region::select_interacting_generic (const Edges &other, bool inverse) -{ - // shortcut - if (other.empty ()) { - if (! inverse) { - clear (); - } - return; - } else if (empty ()) { - return; - } - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - scanner.insert ((char *) &*p + 1, 1); - } - - other.ensure_valid_merged_edges (); - for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - db::Shapes output (false); - EdgeOrRegionBoxConverter bc; - - if (! inverse) { - region_to_edge_interaction_filter filter (output); - scanner.process (filter, 1, bc); - } else { - region_to_edge_interaction_filter filter (output, *this); - scanner.process (filter, 1, bc); - filter.fill_output (); - } - - m_polygons.swap (output); - set_valid_polygons (); -} - -EdgePairs -Region::grid_check (db::Coord gx, db::Coord gy) const -{ - EdgePairs out; - - gx = std::max (db::Coord (1), gx); - gy = std::max (db::Coord (1), gy); - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - - for (size_t i = 0; i < p->holes () + 1; ++i) { - - db::Polygon::polygon_contour_iterator b, e; - - if (i == 0) { - b = p->begin_hull (); - e = p->end_hull (); - } else { - b = p->begin_hole ((unsigned int) (i - 1)); - e = p->end_hole ((unsigned int) (i - 1)); - } - - for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { - if (((*pt).x () % gx) != 0 || ((*pt).y () % gy) != 0) { - out.insert (EdgePair (db::Edge (*pt, *pt), db::Edge (*pt, *pt))); - } - } - - } - - } - - return out; -} - -static bool ac_less (double cos_a, bool gt180_a, double cos_b, bool gt180_b) -{ - if (gt180_a != gt180_b) { - return gt180_a < gt180_b; - } else { - if (gt180_a) { - return cos_a < cos_b - 1e-10; - } else { - return cos_a > cos_b + 1e-10; - } - } -} - -EdgePairs -Region::angle_check (double min, double max, bool inverse) const -{ - EdgePairs out; - - double cos_min = cos (std::max (0.0, std::min (360.0, min)) / 180.0 * M_PI); - double cos_max = cos (std::max (0.0, std::min (360.0, max)) / 180.0 * M_PI); - bool gt180_min = min > 180.0; - bool gt180_max = max > 180.0; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - - for (size_t i = 0; i < p->holes () + 1; ++i) { - - const db::Polygon::contour_type *h = 0; - if (i == 0) { - h = &p->hull (); - } else { - h = &p->hole ((unsigned int) (i - 1)); - } - - size_t np = h->size (); - - for (size_t j = 0; j < np; ++j) { - - db::Edge e ((*h) [j], (*h) [(j + 1) % np]); - db::Edge ee (e.p2 (), (*h) [(j + 2) % np]); - double le = e.double_length (); - double lee = ee.double_length (); - - double cos_a = -db::sprod (e, ee) / (le * lee); - bool gt180_a = db::vprod_sign (e, ee) > 0; - - if ((ac_less (cos_a, gt180_a, cos_max, gt180_max) && !ac_less (cos_a, gt180_a, cos_min, gt180_min)) == !inverse) { - out.insert (EdgePair (e, ee)); - } - - } - - } - - } - - return out; -} - -static inline db::Coord snap_to_grid (db::Coord c, db::Coord g) -{ - // This form of snapping always snaps g/2 to right/top. - if (c < 0) { - c = -g * ((-c + (g - 1) / 2) / g); - } else { - c = g * ((c + g / 2) / g); - } - return c; -} - -void -Region::snap (db::Coord gx, db::Coord gy) -{ - db::Shapes polygons (false); - - gx = std::max (db::Coord (1), gx); - gy = std::max (db::Coord (1), gy); - - std::vector pts; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - - db::Polygon pnew; - - for (size_t i = 0; i < p->holes () + 1; ++i) { - - pts.clear (); - - db::Polygon::polygon_contour_iterator b, e; - - if (i == 0) { - b = p->begin_hull (); - e = p->end_hull (); - } else { - b = p->begin_hole ((unsigned int) (i - 1)); - e = p->end_hole ((unsigned int) (i - 1)); - } - - for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { - pts.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); - } - - if (i == 0) { - pnew.assign_hull (pts.begin (), pts.end ()); - } else { - pnew.insert_hole (pts.begin (), pts.end ()); - } - - } - - polygons.insert (pnew); - - } - - m_polygons.swap (polygons); - - bool was_merged = m_merged_semantics; - invalidate_cache (); - m_is_merged = was_merged; - set_valid_polygons (); -} - -/** - * @brief A helper class to implement the strange polygon detector - */ -struct StrangePolygonInsideFunc -{ - inline bool operator() (int wc) const - { - return wc < 0 || wc > 1; - } -}; - -Region -Region::strange_polygon_check () const -{ - EdgeProcessor ep; - Region out; - - for (const_iterator p = begin (); ! p.at_end (); ++p) { - - ep.clear (); - ep.insert (*p); - - StrangePolygonInsideFunc inside; - db::GenericMerge op (inside); - RegionPolygonSink pc (out); - db::PolygonGenerator pg (pc, false, false); - ep.process (pg, op); - } - - return out; -} - -void -Region::init () -{ - m_report_progress = false; - m_bbox_valid = true; - m_is_merged = true; - m_merged_semantics = true; - m_strict_handling = false; - m_merge_min_coherence = false; - m_merged_polygons_valid = false; -} - -void -Region::disable_progress () -{ - m_report_progress = false; -} - -void -Region::enable_progress (const std::string &progress_desc) -{ - m_report_progress = true; - m_progress_desc = progress_desc; -} - -void -Region::invalidate_cache () -{ - m_bbox_valid = false; - m_merged_polygons.clear (); - m_merged_polygons_valid = false; -} - -void -Region::ensure_valid_merged_polygons () const -{ - // If no merged semantics applies or we will deliver the original - // polygons as merged ones, we need to make sure those are valid - // ones (with a unique memory address) - if (! m_merged_semantics || m_is_merged) { - ensure_valid_polygons (); - } else { - ensure_merged_polygons_valid (); - } -} - -void -Region::ensure_valid_polygons () const -{ - if (! has_valid_polygons ()) { - - m_polygons.clear (); - - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - ++n; - } - m_polygons.reserve (db::Polygon::tag (), n); - - for (const_iterator p = begin (); ! p.at_end (); ++p) { - m_polygons.insert (*p); - } - - // set valid polygons - m_iter = db::RecursiveShapeIterator (); - - } -} - -void -Region::set_valid_polygons () -{ - m_iter = db::RecursiveShapeIterator (); -} - -void -Region::ensure_bbox_valid () const -{ - if (! m_bbox_valid) { - m_bbox = db::Box (); - for (const_iterator p = begin (); ! p.at_end (); ++p) { - m_bbox += p->box (); - } - m_bbox_valid = true; - } -} - -Region::const_iterator -Region::begin_merged () const -{ - if (! m_merged_semantics || m_is_merged) { - return begin (); - } else { - ensure_merged_polygons_valid (); - return db::RegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); - } -} - -std::pair -Region::begin_iter () const -{ - if (has_valid_polygons ()) { - return std::make_pair (db::RecursiveShapeIterator (m_polygons), db::ICplxTrans ()); - } else { - return std::make_pair (m_iter, m_iter_trans); - } -} - -std::pair -Region::begin_merged_iter () const -{ - if (! m_merged_semantics || m_is_merged) { - return begin_iter (); - } else { - ensure_merged_polygons_valid (); - return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); - } -} - -void -Region::ensure_merged_polygons_valid () const -{ - if (! m_merged_polygons_valid) { - - m_merged_polygons.clear (); - - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (0); - db::ShapeGenerator pc (m_merged_polygons); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_merge_min_coherence); - ep.process (pg, op); - - m_merged_polygons_valid = true; - - } -} - -void -Region::insert (const db::Box &box) -{ - if (! box.empty () && box.width () > 0 && box.height () > 0) { - ensure_valid_polygons (); - m_polygons.insert (db::Polygon (box)); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Region::insert (const db::Path &path) -{ - if (path.points () > 0) { - ensure_valid_polygons (); - m_polygons.insert (path.polygon ()); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Region::insert (const db::Polygon &polygon) -{ - if (polygon.holes () > 0 || polygon.vertices () > 0) { - ensure_valid_polygons (); - m_polygons.insert (polygon); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Region::insert (const db::SimplePolygon &polygon) -{ - if (polygon.vertices () > 0) { - ensure_valid_polygons (); - db::Polygon poly; - poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); - m_polygons.insert (poly); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Region::insert (const db::Shape &shape) -{ - if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - ensure_valid_polygons (); - db::Polygon poly; - shape.polygon (poly); - m_polygons.insert (poly); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Region::clear () -{ - m_polygons.clear (); - m_bbox = db::Box (); - m_bbox_valid = true; - m_is_merged = true; - m_merged_polygons.clear (); - m_merged_polygons_valid = true; - m_iter = db::RecursiveShapeIterator (); - m_iter_trans = db::ICplxTrans (); -} - -EdgePairs -Region::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const -{ - EdgePairs result; - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + (other ? other->size () : 0)); - - ensure_valid_merged_polygons (); - size_t n = 0; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - scanner.insert (&*p, n); - n += 2; - } - - if (other) { - other->ensure_valid_merged_polygons (); - n = 1; - for (const_iterator p = other->begin_merged (); ! p.at_end (); ++p) { - scanner.insert (&*p, n); - n += 2; - } - } - - EdgeRelationFilter check (rel, d, metrics); - check.set_include_zero (other != 0); - check.set_whole_edges (whole_edges); - check.set_ignore_angle (ignore_angle); - check.set_min_projection (min_projection); - check.set_max_projection (max_projection); - - Edge2EdgeCheck edge_check (check, result, different_polygons, other != 0); - Poly2PolyCheck poly_check (edge_check); - - do { - scanner.process (poly_check, d, db::box_convert ()); - } while (edge_check.prepare_next_pass ()); - - return result; -} - -EdgePairs -Region::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const -{ - EdgePairs result; - - EdgeRelationFilter check (rel, d, metrics); - check.set_whole_edges (whole_edges); - check.set_ignore_angle (ignore_angle); - check.set_min_projection (min_projection); - check.set_max_projection (max_projection); - - Edge2EdgeCheck edge_check (check, result, false, false); - Poly2PolyCheck poly_check (edge_check); - - do { - size_t n = 0; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - poly_check.finish (&*p, n); - n += 2; - } - } while (edge_check.prepare_next_pass ()); - - return result; -} - -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@q -#endif - } namespace tl diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 1ab534af0..a8b08eee0 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -25,20 +25,10 @@ #define HDR_dbRegion #include "dbCommon.h" - -#include "dbTypes.h" -#include "dbPolygon.h" -#include "dbPath.h" -#include "dbTrans.h" -#include "dbShape.h" -#include "dbShapes.h" -#include "dbShapes2.h" -#include "dbEdgePairRelations.h" -#include "dbShapeProcessor.h" -#include "dbEdges.h" +#include "dbRegionDelegate.h" #include "dbRecursiveShapeIterator.h" -#include "dbEdgePairs.h" -#include "tlString.h" +#include "dbPolygonGenerators.h" + #include "gsiObject.h" #include @@ -46,6 +36,8 @@ namespace db { class EdgeFilterBase; +class FlatRegion; +class EmptyRegion; /** * @brief A base class for polygon filters @@ -291,23 +283,6 @@ private: parameter_type m_parameter; }; -/** - * @brief The region iterator delegate - */ -class DB_PUBLIC RegionIteratorDelegate -{ -public: - RegionIteratorDelegate () { } - virtual ~RegionIteratorDelegate () { } - - typedef db::Polygon value_type; - - virtual bool at_end () const = 0; - virtual void increment () = 0; - virtual const value_type *get () const = 0; - virtual RegionIteratorDelegate *clone () const = 0; -}; - /** * @brief A region iterator * @@ -412,599 +387,6 @@ private: RegionIteratorDelegate *mp_delegate; }; -/** - * @brief The delegate for the actual region implementation - */ -class DB_PUBLIC RegionDelegate -{ -public: - typedef db::Coord coord_type; - typedef db::coord_traits coord_traits; - typedef db::Polygon polygon_type; - typedef db::Vector vector_type; - typedef db::Point point_type; - typedef db::Box box_type; - typedef coord_traits::distance_type distance_type; - typedef coord_traits::perimeter_type perimeter_type; - typedef coord_traits::area_type area_type; - - RegionDelegate (); - virtual ~RegionDelegate (); - - RegionDelegate (const RegionDelegate &other); - RegionDelegate &operator= (const RegionDelegate &other); - - virtual RegionDelegate *clone () const = 0; - - void enable_progress (const std::string &progress_desc); - void disable_progress (); - - void set_min_coherence (bool f); - bool min_coherence () const - { - return m_merge_min_coherence; - } - - void set_merged_semantics (bool f); - bool merged_semantics () const - { - return m_merged_semantics; - } - - void set_strict_handling (bool f); - bool strict_handling () const - { - return m_strict_handling; - } - - virtual std::string to_string (size_t nmax) const = 0; - - virtual RegionIteratorDelegate *begin () const = 0; - virtual RegionIteratorDelegate *begin_merged () const = 0; - - virtual std::pair begin_iter () const = 0; - virtual std::pair begin_merged_iter () const = 0; - - virtual bool empty () const = 0; - virtual bool is_box () const = 0; - virtual bool is_merged () const = 0; - virtual size_t size () const = 0; - - virtual area_type area (const db::Box &box) const = 0; - virtual perimeter_type perimeter (const db::Box &box) const = 0; - virtual Box bbox () const = 0; - - virtual EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; - virtual EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; - virtual EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; - virtual EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; - virtual EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; - virtual EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; - virtual EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; - virtual EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; - virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const = 0; - virtual EdgePairs angle_check (double min, double max, bool inverse) const = 0; - - virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) = 0; - virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy) = 0; - - virtual Edges edges (const EdgeFilterBase *filter) const = 0; - virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) = 0; - virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0; - - virtual RegionDelegate *merged_in_place () = 0; - virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc) = 0; - virtual RegionDelegate *merged () const = 0; - virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const = 0; - - virtual RegionDelegate *strange_polygon_check () const = 0; - - virtual RegionDelegate *sized (coord_type d, unsigned int mode) const = 0; - virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const = 0; - - virtual RegionDelegate *and_with (const Region &other) const = 0; - virtual RegionDelegate *not_with (const Region &other) const = 0; - virtual RegionDelegate *xor_with (const Region &other) const = 0; - virtual RegionDelegate *or_with (const Region &other) const = 0; - virtual RegionDelegate *add_in_place (const Region &other) = 0; - virtual RegionDelegate *add (const Region &other) const = 0; - - virtual RegionDelegate *selected_outside (const Region &other) const = 0; - virtual RegionDelegate *selected_not_outside (const Region &other) const = 0; - virtual RegionDelegate *selected_inside (const Region &other) const = 0; - virtual RegionDelegate *selected_not_inside (const Region &other) const = 0; - virtual RegionDelegate *selected_interacting (const Region &other) const = 0; - virtual RegionDelegate *selected_not_interacting (const Region &other) const = 0; - virtual RegionDelegate *selected_interacting (const Edges &other) const = 0; - virtual RegionDelegate *selected_not_interacting (const Edges &other) const = 0; - virtual RegionDelegate *selected_overlapping (const Region &other) const = 0; - virtual RegionDelegate *selected_not_overlapping (const Region &other) const = 0; - - virtual RegionDelegate *holes () const = 0; - virtual RegionDelegate *hulls () const = 0; - virtual RegionDelegate *in (const Region &other, bool invert) const = 0; - virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const = 0; - virtual RegionDelegate *smoothed (coord_type d) const = 0; - - virtual const db::Polygon *nth (size_t n) const = 0; - virtual bool has_valid_polygons () const = 0; - virtual bool has_valid_merged_polygons () const = 0; - - virtual const db::RecursiveShapeIterator *iter () const = 0; - - virtual bool equals (const Region &other) const = 0; - virtual bool less (const Region &other) const = 0; - -protected: - const std::string &progress_desc () const - { - return m_progress_desc; - } - - bool report_progress () const - { - return m_report_progress; - } - - virtual void merged_semantics_changed () { } - -private: - bool m_merged_semantics; - bool m_strict_handling; - bool m_merge_min_coherence; - bool m_report_progress; - std::string m_progress_desc; -}; - -/** - * @brief An empty Region - */ -class DB_PUBLIC EmptyRegion - : public RegionDelegate -{ -public: - EmptyRegion (); - virtual ~EmptyRegion (); - - EmptyRegion (const EmptyRegion &other); - RegionDelegate *clone () const; - - virtual RegionIteratorDelegate *begin () const { return 0; } - virtual RegionIteratorDelegate *begin_merged () const { return 0; } - - virtual std::pair begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } - virtual std::pair begin_merged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } - - virtual bool empty () const { return true; } - virtual size_t size () const { return 0; } - virtual std::string to_string (size_t) const { return std::string (); } - - virtual bool is_box () const { return false; } - virtual bool is_merged () const { return true; } - virtual area_type area (const db::Box &) const { return 0; } - virtual perimeter_type perimeter (const db::Box &) const { return 0; } - - virtual Box bbox () const { return Box (); } - - virtual EdgePairs width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } - virtual EdgePairs space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } - virtual EdgePairs isolated_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } - virtual EdgePairs notch_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } - virtual EdgePairs enclosing_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } - virtual EdgePairs overlap_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } - virtual EdgePairs separation_check (const Region &, db::Coord, bool , metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } - virtual EdgePairs inside_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } - virtual EdgePairs grid_check (db::Coord, db::Coord) const { return EdgePairs (); } - virtual EdgePairs angle_check (double, double, bool) const { return EdgePairs (); } - - virtual RegionDelegate *snapped_in_place (db::Coord, db::Coord) { return this; } - virtual RegionDelegate *snapped (db::Coord, db::Coord) { return new EmptyRegion (); } - - virtual RegionDelegate *strange_polygon_check () const { return new EmptyRegion (); } - - virtual Edges edges (const EdgeFilterBase *) const { return db::Edges (); } - virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; } - virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); } - - virtual RegionDelegate *merged_in_place () { return this; } - virtual RegionDelegate *merged_in_place (bool, unsigned int) { return this; } - virtual RegionDelegate *merged () const { return new EmptyRegion (); } - virtual RegionDelegate *merged (bool, unsigned int) const { return new EmptyRegion (); } - - virtual RegionDelegate *sized (coord_type, unsigned int) const { return new EmptyRegion (); } - virtual RegionDelegate *sized (coord_type, coord_type, unsigned int) const { return new EmptyRegion (); } - - virtual RegionDelegate *and_with (const Region &) const { return new EmptyRegion (); } - virtual RegionDelegate *not_with (const Region &) const { return new EmptyRegion (); } - virtual RegionDelegate *xor_with (const Region &other) const; - virtual RegionDelegate *or_with (const Region &other) const; - virtual RegionDelegate *add_in_place (const Region &other); - virtual RegionDelegate *add (const Region &other) const; - - virtual RegionDelegate *selected_outside (const Region &) const { return new EmptyRegion (); } - virtual RegionDelegate *selected_not_outside (const Region &) const { return new EmptyRegion (); } - virtual RegionDelegate *selected_inside (const Region &) const { return new EmptyRegion (); } - virtual RegionDelegate *selected_not_inside (const Region &) const { return new EmptyRegion (); } - virtual RegionDelegate *selected_interacting (const Region &) const { return new EmptyRegion (); } - virtual RegionDelegate *selected_not_interacting (const Region &) const { return new EmptyRegion (); } - virtual RegionDelegate *selected_interacting (const Edges &) const { return new EmptyRegion (); } - virtual RegionDelegate *selected_not_interacting (const Edges &) const { return new EmptyRegion (); } - virtual RegionDelegate *selected_overlapping (const Region &) const { return new EmptyRegion (); } - virtual RegionDelegate *selected_not_overlapping (const Region &) const { return new EmptyRegion (); } - - virtual RegionDelegate *holes () const { return new EmptyRegion (); } - virtual RegionDelegate *hulls () const { return new EmptyRegion (); } - virtual RegionDelegate *in (const Region &, bool) const { return new EmptyRegion (); } - virtual RegionDelegate *rounded_corners (double, double, unsigned int) const { return new EmptyRegion (); } - virtual RegionDelegate *smoothed (coord_type) const { return new EmptyRegion (); } - - virtual bool has_valid_polygons () const { return true; } - virtual bool has_valid_merged_polygons () const { return true; } - virtual const db::Polygon *nth (size_t) const { tl_assert (false); } - - virtual const db::RecursiveShapeIterator *iter () const { return 0; } - - virtual bool equals (const Region &other) const; - virtual bool less (const Region &other) const; - -private: - EmptyRegion &operator= (const EmptyRegion &other); -}; - -/** - * @brief Provides default flat implementations - */ -class DB_PUBLIC AsIfFlatRegion - : public RegionDelegate -{ -public: - AsIfFlatRegion (); - virtual ~AsIfFlatRegion (); - - virtual bool is_box () const; - virtual size_t size () const; - - virtual area_type area (const db::Box &box) const; - virtual perimeter_type perimeter (const db::Box &box) const; - virtual Box bbox () const; - - virtual std::string to_string (size_t nmax) const; - - EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const - { - return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const - { - return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const - { - return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const - { - return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const - { - return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const - { - return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const - { - return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const - { - return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const; - virtual EdgePairs angle_check (double min, double max, bool inverse) const; - - virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) - { - return snapped (gx, gy); - } - - virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); - - virtual Edges edges (const EdgeFilterBase *) const; - - virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) - { - return filtered (filter); - } - - virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const; - - virtual RegionDelegate *merged_in_place () - { - return merged (); - } - - virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc) - { - return merged (min_coherence, min_wc); - } - - virtual RegionDelegate *merged () const - { - return merged (min_coherence (), 0); - } - - virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const; - - virtual RegionDelegate *strange_polygon_check () const; - - virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; - virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; - - virtual RegionDelegate *and_with (const Region &other) const; - virtual RegionDelegate *not_with (const Region &other) const; - virtual RegionDelegate *xor_with (const Region &other) const; - virtual RegionDelegate *or_with (const Region &other) const; - - virtual RegionDelegate *add_in_place (const Region &other) - { - return add (other); - } - - virtual RegionDelegate *add (const Region &other) const; - - virtual RegionDelegate *selected_outside (const Region &other) const - { - return selected_interacting_generic (other, 1, false, false); - } - - virtual RegionDelegate *selected_not_outside (const Region &other) const - { - return selected_interacting_generic (other, 1, false, true); - } - - virtual RegionDelegate *selected_inside (const Region &other) const - { - return selected_interacting_generic (other, -1, true, false); - } - - virtual RegionDelegate *selected_not_inside (const Region &other) const - { - return selected_interacting_generic (other, -1, true, true); - } - - virtual RegionDelegate *selected_interacting (const Region &other) const - { - return selected_interacting_generic (other, 0, true, false); - } - - virtual RegionDelegate *selected_not_interacting (const Region &other) const - { - return selected_interacting_generic (other, 0, true, true); - } - - virtual RegionDelegate *selected_interacting (const Edges &other) const - { - return selected_interacting_generic (other, false); - } - - virtual RegionDelegate *selected_not_interacting (const Edges &other) const - { - return selected_interacting_generic (other, true); - } - - virtual RegionDelegate *selected_overlapping (const Region &other) const - { - return selected_interacting_generic (other, 0, false, false); - } - - virtual RegionDelegate *selected_not_overlapping (const Region &other) const - { - return selected_interacting_generic (other, 0, false, true); - } - - virtual RegionDelegate *holes () const; - virtual RegionDelegate *hulls () const; - virtual RegionDelegate *in (const Region &other, bool invert) const; - virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; - virtual RegionDelegate *smoothed (coord_type d) const; - - virtual bool equals (const Region &other) const; - virtual bool less (const Region &other) const; - -protected: - void update_bbox (const db::Box &box); - void invalidate_bbox (); - -private: - AsIfFlatRegion &operator= (const AsIfFlatRegion &other); - - mutable bool m_bbox_valid; - mutable db::Box m_bbox; - - virtual db::Box compute_bbox () const; - static RegionDelegate *region_from_box (const db::Box &b); - - EdgePairs run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - EdgePairs run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; - RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const; -}; - -/** - * @brief A flat, polygon-set delegate - */ -class DB_PUBLIC FlatRegion - : public AsIfFlatRegion -{ -public: - typedef db::layer polygon_layer_type; - typedef polygon_layer_type::iterator polygon_iterator_type; - - FlatRegion (); - FlatRegion (const db::Shapes &polygons, bool is_merged); - FlatRegion (bool is_merged); - - FlatRegion (const FlatRegion &other); - - virtual ~FlatRegion (); - - RegionDelegate *clone () const - { - return new FlatRegion (*this); - } - - void reserve (size_t); - - virtual RegionIteratorDelegate *begin () const; - virtual RegionIteratorDelegate *begin_merged () const; - - virtual std::pair begin_iter () const; - virtual std::pair begin_merged_iter () const; - - virtual bool empty () const; - virtual size_t size () const; - virtual bool is_merged () const; - - virtual RegionDelegate *merged_in_place (); - virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); - virtual RegionDelegate *merged () const; - - virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter); - - virtual RegionDelegate *add_in_place (const Region &other); - virtual RegionDelegate *add (const Region &other) const; - - virtual const db::Polygon *nth (size_t n) const; - virtual bool has_valid_polygons () const; - virtual bool has_valid_merged_polygons () const; - - virtual const db::RecursiveShapeIterator *iter () const; - - void insert (const db::Box &box); - void insert (const db::Path &path); - void insert (const db::SimplePolygon &polygon); - void insert (const db::Polygon &polygon); - void insert (const db::Shape &shape); - - template - void insert (const db::Shape &shape, const T &trans) - { - if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - db::Polygon poly; - shape.polygon (poly); - poly.transform (trans); - insert (poly); - } - } - - template - void insert (const Iter &b, const Iter &e) - { - reserve (size () + (e - b)); - for (Iter i = b; i != e; ++i) { - insert (*i); - } - } - - template - void insert_seq (const Iter &seq) - { - for (Iter i = seq; ! i.at_end (); ++i) { - insert (*i); - } - } - - template - void transform (const Trans &trans) - { - if (! trans.is_unity ()) { - for (polygon_iterator_type p = m_polygons.get_layer ().begin (); p != m_polygons.get_layer ().end (); ++p) { - m_polygons.get_layer ().replace (p, p->transformed (trans)); - } - invalidate_cache (); - } - } - -protected: - virtual void merged_semantics_changed (); - virtual Box compute_bbox () const; - void invalidate_cache (); - void set_is_merged (bool m); - -private: - friend class AsIfFlatRegion; - - db::Shapes &raw_polygons () { return m_polygons; } - - FlatRegion &operator= (const FlatRegion &other); - - bool m_is_merged; - mutable db::Shapes m_polygons; - mutable db::Shapes m_merged_polygons; - mutable bool m_merged_polygons_valid; - - void init (); - void ensure_merged_polygons_valid () const; -}; - -/** - * @brief An original layerregion based on a RecursiveShapeIterator - */ -class DB_PUBLIC OriginalLayerRegion - : public AsIfFlatRegion -{ -public: - OriginalLayerRegion (); - OriginalLayerRegion (const OriginalLayerRegion &other); - OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged = false); - OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); - virtual ~OriginalLayerRegion (); - - RegionDelegate *clone () const; - - virtual RegionIteratorDelegate *begin () const; - virtual RegionIteratorDelegate *begin_merged () const; - - virtual std::pair begin_iter () const; - virtual std::pair begin_merged_iter () const; - - virtual bool empty () const; - - virtual bool is_merged () const; - - virtual const db::Polygon *nth (size_t n) const; - virtual bool has_valid_polygons () const; - virtual bool has_valid_merged_polygons () const; - - virtual const db::RecursiveShapeIterator *iter () const; - - virtual bool equals (const Region &other) const; - virtual bool less (const Region &other) const; - -private: - FlatRegion &operator= (const FlatRegion &other); - - bool m_is_merged; - mutable db::Shapes m_merged_polygons; - mutable bool m_merged_polygons_valid; - mutable db::RecursiveShapeIterator m_iter; - db::ICplxTrans m_iter_trans; - - void init (); - void ensure_merged_polygons_valid () const; -}; - /** * @brief A helper class allowing delivery of addressable polygons * @@ -1126,9 +508,7 @@ public: template Region (const Sh &s) { - FlatRegion *delegate = new FlatRegion (); - mp_delegate = delegate; - delegate->insert (s); + insert (s); } /** @@ -1141,9 +521,10 @@ public: template Region (const Iter &b, const Iter &e) { - FlatRegion *delegate = new FlatRegion (); - mp_delegate = delegate; - delegate->insert (b, e); + reserve (e - b); + for (Iter i = b; i != e; ++i) { + insert (*i); + } } /** @@ -1230,27 +611,18 @@ public: * @brief Inserts the given shape (working object) into the region */ template - void insert (const Sh &shape) - { - flat_region ()->insert (shape); - } + void insert (const Sh &shape); /** * @brief Insert a shape reference into the region */ - void insert (const db::Shape &shape) - { - flat_region ()->insert (shape); - } + void insert (const db::Shape &shape); /** * @brief Insert a transformed shape into the region */ template - void insert (const db::Shape &shape, const T &trans) - { - flat_region ()->insert (shape, trans); - } + void insert (const db::Shape &shape, const T &trans); /** * @brief Returns true if the region is empty @@ -1281,18 +653,12 @@ public: /** * @brief Clear the region */ - void clear () - { - set_delegate (new EmptyRegion ()); - } + void clear (); /** * @brief Reserve memory for the given number of polygons */ - void reserve (size_t n) - { - flat_region ()->reserve (n); - } + void reserve (size_t n); /** * @brief Sets the minimum-coherence flag @@ -1602,11 +968,7 @@ public: * @brief Transform the region */ template - Region &transform (const T &trans) - { - flat_region ()->transform (trans); - return *this; - } + Region &transform (const T &trans); /** * @brief Returns the transformed region @@ -2301,1213 +1663,6 @@ private: FlatRegion *flat_region (); }; - -// ...................................................................................................... - -#if 0 // ORIGINAL -/** - * @brief A region - * - * A region basically is a set of polygons. It supports a variety of operations, i.e. - * boolean operations with other regions, sizing etc. - * - * Regions can have different states. Specifically a region can be merged (no overlapping - * polygons are present, touching polygons are merged, self-intersections of polygons are - * removed) or non-merged (polygons may overlap or polygons may be self-intersecting). In - * merged state, the wrap count at every point is either zero or 1, in non-merged state - * it can be every value. - * - * Polygons inside the region may contain holes if the region is merged. - */ -class DB_PUBLIC Region - : public gsi::ObjectBase -{ -public: - typedef db::Coord coord_type; - typedef db::coord_traits coord_traits; - typedef db::Polygon polygon_type; - typedef db::Vector vector_type; - typedef db::Point point_type; - typedef db::Box box_type; - typedef coord_traits::distance_type distance_type; - typedef coord_traits::perimeter_type perimeter_type; - typedef coord_traits::area_type area_type; - typedef RegionIterator const_iterator; - - /** - * @brief Default constructor - * - * Creates an empty region. - */ - Region () - : m_polygons (false), m_merged_polygons (false) - { - init (); - } - - /** - * @brief Constructor from an object - * - * Creates a region representing a single instance of that object - */ - template - Region (const Sh &s) - : m_polygons (false), m_merged_polygons (false) - { - init (); - insert (s); - } - - /** - * @brief Sequence constructor - * - * Creates a region from a sequence of objects. The objects can be boxes, - * polygons, paths or shapes. This version accepts iterators of the begin ... end - * style. - */ - template - Region (const Iter &b, const Iter &e) - : m_polygons (false), m_merged_polygons (false) - { - init (); - reserve (e - b); - for (Iter i = b; i != e; ++i) { - insert (*i); - } - } - - /** - * @brief Constructor from a RecursiveShapeIterator - * - * Creates a region from a recursive shape iterator. This allows to feed a region - * from a hierarchy of cells. - */ - Region (const RecursiveShapeIterator &si); - - /** - * @brief Constructor from a RecursiveShapeIterator with a transformation - * - * Creates a region from a recursive shape iterator. This allows to feed a region - * from a hierarchy of cells. The transformation is useful to scale to a specific - * DBU for example. - */ - Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); - - /** - * @brief Enable progress reporting - * - * @param progress_text The description text of the progress object - */ - void enable_progress (const std::string &progress_desc = std::string ()); - - /** - * @brief Disable progress reporting - */ - void disable_progress (); - - /** - * @brief Iterator of the region - * - * The iterator delivers the polygons of the region. - * It follows the at_end semantics. - */ - const_iterator begin () const - { - if (has_valid_polygons ()) { - return const_iterator (m_polygons.get_layer ().begin (), m_polygons.get_layer ().end ()); - } else { - return const_iterator (m_iter, m_iter_trans); - } - } - - /** - * @brief Returns the merged polygons if merge semantics applies - * - * If merge semantics is not enabled, this iterator delivers the individual polygons. - */ - const_iterator begin_merged () const; - - /** - * @brief Delivers a RecursiveShapeIterator pointing to the polygons plus the necessary transformation - */ - std::pair begin_iter () const; - - /** - * @brief Delivers a RecursiveShapeIterator pointing to the merged polygons plus the necessary transformation - */ - std::pair begin_merged_iter () const; - - /** - * @brief Insert a box into the region - */ - void insert (const db::Box &box); - - /** - * @brief Insert a path into the region - */ - void insert (const db::Path &path); - - /** - * @brief Insert a simple polygon into the region - */ - void insert (const db::SimplePolygon &polygon); - - /** - * @brief Insert a polygon into the region - */ - void insert (const db::Polygon &polygon); - - /** - * @brief Insert a shape into the region - */ - void insert (const db::Shape &shape); - - /** - * @brief Insert a transformed shape into the region - */ - template - void insert (const db::Shape &shape, const T &trans) - { - if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - ensure_valid_polygons (); - db::Polygon poly; - shape.polygon (poly); - poly.transform (trans); - m_polygons.insert (poly); - m_is_merged = false; - invalidate_cache (); - } - } - - /** - * @brief Returns true if the region is empty - */ - bool empty () const - { - return has_valid_polygons () && m_polygons.empty (); - } - - /** - * @brief Returns the number of polygons in the region - */ - size_t size () const; - - /** - * @brief Returns a string representing the region - * - * nmax specifies how many polygons are included (set to std::numeric_limits::max() for "all". - */ - std::string to_string (size_t nmax = 10) const; - - /** - * @brief Clear the region - */ - void clear (); - - /** - * @brief Reserve memory for the given number of polygons - */ - void reserve (size_t n) - { - m_polygons.reserve (db::Polygon::tag (), n); - } - - /** - * @brief Sets the minimum-coherence flag - * - * If minimum coherence is set, the merge operations (explicit merge with \merge or - * implicit merge through merged_semantics) are performed using minimum coherence mode. - * The coherence mode determines how kissing-corner situations are resolved. If - * minimum coherence is selected, they are resolved such that multiple polygons are - * created which touch at a corner). - */ - void set_min_coherence (bool f) - { - if (m_merge_min_coherence != f) { - m_merge_min_coherence = f; - invalidate_cache (); - } - } - - /** - * @brief Gets the minimum coherence flag - */ - bool min_coherence () const - { - return m_merge_min_coherence; - } - - /** - * @brief Sets the merged-semantics flag - * - * If merged semantics is enabled (the default), coherent polygons will be considered - * as single regions and artificial edges such as cut-lines will not be considered. - * Merged semantics thus is equivalent to considering coherent areas rather than - * single polygons. - */ - void set_merged_semantics (bool f); - - /** - * @brief Gets the merged-semantics flag - */ - bool merged_semantics () const - { - return m_merged_semantics; - } - - /** - * @brief Enables or disables strict handling - * - * Strict handling means to leave away some optimizations. Specifically the - * output of boolean operations will be merged even if one input is empty. - * Without strict handling, the operation will be optimized and output - * won't be merged. - * - * Strict handling is disabled by default. - */ - void set_strict_handling (bool f); - - /** - * @brief Gets a valid indicating whether strict handling is enabled - */ - bool strict_handling () const - { - return m_strict_handling; - } - - /** - * @brief Returns true if the region is a single box - * - * If the region is not merged, this method may return false even - * if the merged region would be a box. - */ - bool is_box () const; - - /** - * @brief Returns true if the region is merged - */ - bool is_merged () const - { - return m_is_merged; - } - - /** - * @brief Returns the area of the region - * - * This method returns the area sum over all polygons. - * Merged semantics applies. In merged semantics, the area is the correct area covered by the - * polygons. Without merged semantics, overlapping parts are counted twice. - * - * If a box is given, the computation is restricted to that box. - */ - area_type area (const db::Box &box = db::Box ()) const; - - /** - * @brief Returns the perimeter sum of the region - * - * This method returns the perimeter sum over all polygons. - * Merged semantics applies. In merged semantics, the perimeter is the true perimeter. - * Without merged semantics, inner edges contribute to the perimeter. - * - * If a box is given, the computation is restricted to that box. - * Edges coincident with the box edges are counted only if the form outer edges at the box edge. - */ - perimeter_type perimeter (const db::Box &box = db::Box ()) const; - - /** - * @brief Returns the bounding box of the region - */ - Box bbox () const - { - ensure_bbox_valid (); - return m_bbox; - } - - /** - * @brief Filters the polygons - * - * This method will keep all polygons for which the filter returns true. - * Merged semantics applies. In merged semantics, the filter will run over - * all merged polygons. - */ - template - Region &filter (F &filter) - { - polygon_iterator_type pw = m_polygons.get_layer ().begin (); - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - if (filter (*p)) { - if (pw == m_polygons.get_layer ().end ()) { - m_polygons.get_layer ().insert (*p); - pw = m_polygons.get_layer ().end (); - } else { - m_polygons.get_layer ().replace (pw++, *p); - } - } - } - m_polygons.get_layer ().erase (pw, m_polygons.get_layer ().end ()); - m_merged_polygons.clear (); - m_is_merged = m_merged_semantics; - m_iter = db::RecursiveShapeIterator (); - return *this; - } - - /** - * @brief Returns the filtered polygons - * - * This method will return a new region with only those polygons which - * conform to the filter criterion. - */ - template - Region filtered (F &filter) const - { - Region d; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - if (filter (*p)) { - d.insert (*p); - } - } - return d; - } - - /** - * @brief Applies a width check and returns EdgePairs which correspond to violation markers - * - * The width check will create a edge pairs if the width of the area between the - * edges is less than the specified threshold d. Without "whole_edges", the parts of - * the edges are returned which violate the condition. If "whole_edges" is true, the - * result will contain the complete edges participating in the result. - * - * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" - * metrics are available. - * - * ingore_angle allows specification of a maximum angle that connected edges can have to not participate - * in the check. By choosing 90 degree, edges with angles of 90 degree and larger are not checked, - * but acute corners are for example. - * - * With min_projection and max_projection it is possible to specify how edges must be related - * to each other. If the length of the projection of either edge on the other is >= min_projection - * or < max_projection, the edges are considered for the check. - * - * The order of the edges in the resulting edge pairs is undefined. - * - * Merged semantics applies. - */ - EdgePairs width_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a space check and returns EdgePairs which correspond to violation markers - * - * For the parameters see \width_check. The space check reports edges for which the space is - * less than the specified threshold d. - * - * Merged semantics applies. - */ - EdgePairs space_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a isolation check (space of polygon vs. other polygons) and returns EdgePairs which correspond to violation markers - * - * For the parameters see \width_check. The space check reports edges for which the notch space is - * less than the specified threshold d. - * - * Merged semantics applies. - */ - EdgePairs isolated_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a notch check (space in polygon vs. itself) and returns EdgePairs which correspond to violation markers - * - * For the parameters see \width_check. The space check reports edges for which the notch space is - * less than the specified threshold d. - * - * Merged semantics applies. - */ - EdgePairs notch_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a enclosed check and returns EdgePairs which correspond to violation markers - * - * The check will return true, where this region is enclosing the polygons of the other - * region by less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a overlap check and returns EdgePairs which correspond to violation markers - * - * The check will return true, where this region overlaps the polygons of the other - * region by less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a separation check and returns EdgePairs which correspond to violation markers - * - * The check will return true, where this region is separated by polygons of the other - * region by less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a inside check and returns EdgePairs which correspond to violation markers - * - * The check will return true, where this region is inside by less than the threshold d inside the polygons of the other - * region. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Returns an edge set containing all edges of the polygons in this region - * - * Merged semantics applies. In merged semantics, only full, outer edges are delivered. - */ - Edges edges () const; - - /** - * @brief Returns an edge set containing all edges of the polygons in this region - * - * This version allows to specify a filter by which the edges are filtered before they are - * returned. - * - * Merged semantics applies. In merged semantics, only full, outer edges are delivered. - */ - template - Edges edges (F &filter) const - { - Edges edges; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - if (filter (*e)) { - edges.insert (*e); - } - } - } - return edges; - } - - /** - * @brief Transform the region - */ - template - Region &transform (const T &trans) - { - if (! trans.is_unity ()) { - ensure_valid_polygons (); - for (polygon_iterator_type p = m_polygons.get_layer ().begin (); p != m_polygons.get_layer ().end (); ++p) { - m_polygons.get_layer ().replace (p, p->transformed (trans)); - } - m_iter_trans = db::ICplxTrans (trans) * m_iter_trans; - m_bbox_valid = false; - } - return *this; - } - - /** - * @brief Returns the transformed region - */ - template - Region transformed (const T &trans) const - { - Region d (*this); - d.transform (trans); - return d; - } - - /** - * @brief Performs an off-grid check on the polygons inside the region - * - * The method returns single-point edge pairs for each vertex found off-grid. - * The grid can be specified differently in x and y direction. - */ - EdgePairs grid_check (db::Coord gx, db::Coord gy) const; - - /** - * @brief Performs an angle check - * - * The method returns edge pairs for each connected edges having - * an angle between min and max (exclusive max, in degree) or - * not between min and max (if inverse is true). - */ - EdgePairs angle_check (double min, double max, bool inverse) const; - - /** - * @brief Grid-snaps the region - * - * Snaps the vertices of the polygons to the specified grid. - * different grids can be specified int x and y direction. - */ - void snap (db::Coord gx, db::Coord gy); - - /** - * @brief Returns the snapped region - */ - Region snapped (db::Coord gx, db::Coord gy) const - { - Region d (*this); - d.snap (gx, gy); - return d; - } - - /** - * @brief Performs a check for "strange" polygons - * - * This check will return a region with all self-overlapping or - * non-orientable parts of polygons. - * - * Naturally this method will ignore the merged_semantics setting. - */ - Region strange_polygon_check () const; - - /** - * @brief Swap with the other region - */ - void swap (db::Region &other); - - /** - * @brief Merge the region - * - * This method merges the polygons of the region if they are not merged already. - * It returns a reference to this region. - * An out-of-place merge version is "merged". - */ - Region &merge (); - - /* - * @brief Returns the merged region - * - * This is the out-of-place merge. It returns a new region but does not modify - * the region it is called on. An in-place version is "merge". - */ - Region merged () const - { - Region d (*this); - d.merge (); - return d; - } - - /** - * @brief Merge the region with options - * - * This will merge the region and provides some options while doing so. - * - * A result is generated if the wrap count (wc) - * of a point is larger than the given min_wrapcount value. - * A min_wrapcount of 1 will produce output where at least two polygons overlap. - * - * This method will always execute the merge, even if the region is already merged. - * - * @param min_coherence Set this parameter to true to get minimum polygons (kissing corner problem) - * @param min_wrapcount See the description above - * @return A reference to this region - */ - Region &merge (bool min_coherence, unsigned int min_wc = 0); - - /** - * @brief Returns the merged region with options - * - * This is the out-of-place version of "merge" with options (see there). - */ - Region merged (bool min_coherence, unsigned int min_wc = 0) const - { - Region d (*this); - d.merge (min_coherence, min_wc); - return d; - } - - /** - * @brief Size the region - * - * This method applies a sizing to the region. Before the sizing is done, the - * region is merged if this is not the case already. - * - * The result is a set of polygons which may be overlapping, but are not self- - * intersecting. - * - * Merged semantics applies. - * - * @param d The (isotropic) sizing value - * @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance. - * @return A reference to self - */ - Region &size (coord_type d, unsigned int mode = 2) - { - return size (d, d, mode); - } - - /** - * @brief Anisotropic sizing - * - * This version provides anisotropic sizing by allowing to specify a distance int x and y - * direction. - * - * Merged semantics applies. - * - * @param dx The horizontal sizing - * @param dy The vertical sizing - * @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance. - */ - Region &size (coord_type dx, coord_type dy, unsigned int mode = 2); - - /** - * @brief Returns the sized region - * - * This is an out-of-place version of the size method with isotropic sizing - * "merged polygon" semantics applies if merged_polygon_semantics is true (see set_auto_merge). - * - * Merged semantics applies. - */ - Region sized (coord_type d, unsigned int mode = 2) const - { - Region r (*this); - r.size (d, mode); - return r; - } - - /** - * @brief Returns the sized region - * - * This is an out-of-place version of the size method with anisotropic sizing - * "merged polygon" semantics applies if merged_polygon_semantics is true (see set_auto_merge). - * - * Merged semantics applies. - */ - Region sized (coord_type dx, coord_type dy, unsigned int mode = 2) const - { - Region r (*this); - r.size (dx, dy, mode); - return r; - } - - /** - * @brief Boolean AND operator - */ - Region operator& (const Region &other) const - { - Region d (*this); - d &= other; - return d; - } - - /** - * @brief In-place boolean AND operator - * - * This method does not necessarily merge the region. To ensure the region - * is merged, call merge afterwards. - */ - Region &operator&= (const Region &other); - - /** - * @brief Boolean NOT operator - */ - Region operator- (const Region &other) const - { - Region d (*this); - d -= other; - return d; - } - - /** - * @brief In-place boolean NOT operator - * - * This method does not necessarily merge the region. To ensure the region - * is merged, call merge afterwards. - */ - Region &operator-= (const Region &other); - - /** - * @brief Boolean XOR operator - */ - Region operator^ (const Region &other) const - { - Region d (*this); - d ^= other; - return d; - } - - /** - * @brief In-place boolean XOR operator - * - * This method does not necessarily merge the region. To ensure the region - * is merged, call merge afterwards. - */ - Region &operator^= (const Region &other); - - /** - * @brief Boolean OR operator - * - * This method merges the polygons of both regions. - */ - Region operator| (const Region &other) const - { - Region d (*this); - d |= other; - return d; - } - - /** - * @brief In-place boolean OR operator - */ - Region &operator|= (const Region &other); - - /** - * @brief Joining of regions - * - * This method joins the regions but does not merge them afterwards. - */ - Region operator+ (const Region &other) const - { - Region d (*this); - d += other; - return d; - } - - /** - * @brief In-place region joining - */ - Region &operator+= (const Region &other); - - /** - * @brief Selects all polygons of this region which are completly outside polygons from the other region - * - * Merged semantics applies. - */ - Region &select_outside (const Region &other) - { - select_interacting_generic (other, 1, false, false); - return *this; - } - - /** - * @brief Selects all polygons of this region which are not completly outside polygons from the other region - * - * Merged semantics applies. - */ - Region &select_not_outside (const Region &other) - { - select_interacting_generic (other, 1, false, true); - return *this; - } - - /** - * @brief Returns all polygons of this which are completly outside polygons from the other region - * - * This method is an out-of-place version of select_outside. - * - * Merged semantics applies. - */ - Region selected_outside (const Region &other) const - { - return selected_interacting_generic (other, 1, false, false); - } - - /** - * @brief Returns all polygons of this which are not completly outside polygons from the other region - * - * This method is an out-of-place version of select_not_outside. - * - * Merged semantics applies. - */ - Region selected_not_outside (const Region &other) const - { - return selected_interacting_generic (other, 1, false, true); - } - - /** - * @brief Selects all polygons of this region which are completly inside polygons from the other region - * - * Merged semantics applies. - */ - Region &select_inside (const Region &other) - { - select_interacting_generic (other, -1, false, false); - return *this; - } - - /** - * @brief Selects all polygons of this region which are not completly inside polygons from the other region - * - * Merged semantics applies. - */ - Region &select_not_inside (const Region &other) - { - select_interacting_generic (other, -1, false, true); - return *this; - } - - /** - * @brief Returns all polygons of this which are completly inside polygons from the other region - * - * This method is an out-of-place version of select_inside. - * - * Merged semantics applies. - */ - Region selected_inside (const Region &other) const - { - return selected_interacting_generic (other, -1, true, false); - } - - /** - * @brief Returns all polygons of this which are not completly inside polygons from the other region - * - * This method is an out-of-place version of select_not_inside. - * - * Merged semantics applies. - */ - Region selected_not_inside (const Region &other) const - { - return selected_interacting_generic (other, -1, true, true); - } - - /** - * @brief Selects all polygons of this region which overlap or touch polygons from the other region - * - * Merged semantics applies. - */ - Region &select_interacting (const Region &other) - { - select_interacting_generic (other, 0, true, false); - return *this; - } - - /** - * @brief Selects all polygons of this region which do not overlap or touch polygons from the other region - * - * Merged semantics applies. - */ - Region &select_not_interacting (const Region &other) - { - select_interacting_generic (other, 0, true, true); - return *this; - } - - /** - * @brief Returns all polygons of this which overlap or touch polygons from the other region - * - * This method is an out-of-place version of select_interacting. - * - * Merged semantics applies. - */ - Region selected_interacting (const Region &other) const - { - return selected_interacting_generic (other, 0, true, false); - } - - /** - * @brief Returns all polygons of this which do not overlap or touch polygons from the other region - * - * This method is an out-of-place version of select_not_interacting. - * - * Merged semantics applies. - */ - Region selected_not_interacting (const Region &other) const - { - return selected_interacting_generic (other, 0, true, true); - } - - /** - * @brief Selects all polygons of this region which overlap or touch edges from the given edge collection - * - * Merged semantics applies to both operators. - */ - Region &select_interacting (const Edges &other) - { - select_interacting_generic (other, false); - return *this; - } - - /** - * @brief Selects all polygons of this region which do not overlap or touch edges from the edge collection - * - * Merged semantics applies to both operators. - */ - Region &select_not_interacting (const Edges &other) - { - select_interacting_generic (other, true); - return *this; - } - - /** - * @brief Returns all polygons of this which overlap or touch edges from the edge collection - * - * This method is an out-of-place version of select_interacting. - * - * Merged semantics applies to both operators. - */ - Region selected_interacting (const Edges &other) const - { - return selected_interacting_generic (other, false); - } - - /** - * @brief Returns all polygons of this which do not overlap or touch polygons from the other region - * - * This method is an out-of-place version of select_not_interacting. - * - * Merged semantics applies to both operators. - */ - Region selected_not_interacting (const Edges &other) const - { - return selected_interacting_generic (other, true); - } - - /** - * @brief Selects all polygons of this region which overlap polygons from the other region - * - * Merged semantics applies. - */ - Region &select_overlapping (const Region &other) - { - select_interacting_generic (other, 0, false, false); - return *this; - } - - /** - * @brief Selects all polygons of this region which do not overlap polygons from the other region - * - * Merged semantics applies. - */ - Region &select_not_overlapping (const Region &other) - { - select_interacting_generic (other, 0, false, true); - return *this; - } - - /** - * @brief Returns all polygons of this which overlap polygons from the other region - * - * This method is an out-of-place version of select_overlapping. - * - * Merged semantics applies. - */ - Region selected_overlapping (const Region &other) const - { - return selected_interacting_generic (other, 0, false, false); - } - - /** - * @brief Returns all polygons of this which do not overlap polygons from the other region - * - * This method is an out-of-place version of select_not_overlapping. - * - * Merged semantics applies. - */ - Region selected_not_overlapping (const Region &other) const - { - return selected_interacting_generic (other, 0, false, true); - } - - /** - * @brief Returns the holes - * - * This method returns the holes of the polygons. - * - * Merged semantics applies. - */ - Region holes () const; - - /** - * @brief Returns the hulls - * - * This method returns the hulls of the polygons. It does not merge the - * polygons before the hulls are derived. - * - * Merged semantics applies. - */ - Region hulls () const; - - /** - * @brief Returns all polygons which are in the other region - * - * This method will return all polygons which are part of another region. - * The match is done exactly. - * The "invert" flag can be used to invert the sense, i.e. with - * "invert" set to true, this method will return all polygons not - * in the other region. - * - * Merged semantics applies. - */ - Region in (const Region &other, bool invert = false) const; - - /** - * @brief Round corners (in-place) - * - * @param rinner The inner radius in DBU units - * @param router The outer radius in DBU units - * @param n The number of points to use per circle - */ - void round_corners (double rinner, double router, unsigned int n) - { - Region r = rounded_corners (rinner, router, n); - swap (r); - } - - /** - * @brief Returns a new region with rounded corners (out of place) - */ - Region rounded_corners (double rinner, double router, unsigned int n) const; - - /** - * @brief Returns the smoothed region - * - * @param d The smoothing accuracy - */ - Region smoothed (coord_type d) const; - - /** - * @brief Smoothes the region (in-place) - */ - void smooth (coord_type d) - { - Region r = smoothed (d); - swap (r); - } - - /** - * @brief Returns the nth polygon - * - * This method will force the polygons to be inside the polygon vector. - * If that happens, the method may be costly and will invalidate any iterator. - * The iterator should be used whenever possible. - */ - const db::Polygon *nth (size_t n) const - { - ensure_valid_polygons (); - return n < m_polygons.size () ? &m_polygons.get_layer ().begin () [n] : 0; - } - - /** - * @brief Returns true, if the region has valid polygons stored within itself - */ - bool has_valid_polygons () const - { - // Note: we take a copy of the iterator since the at_end method may - // validate the iterator which will make it refer to a specifc configuration. - return db::RecursiveShapeIterator (m_iter).at_end (); - } - - /** - * @brief Gets the internal iterator - * - * This method is intended for users who know what they are doing - */ - const db::RecursiveShapeIterator &iter () const - { - return m_iter; - } - - /** - * @brief Ensures the region has valid polygons - * - * This method is const since it has const semantics. - */ - void ensure_valid_polygons () const; - - /** - * @brief Ensures the region has valid merged polygons - * - * It will make sure that begin_merged will deliver an - * iterator to a polygon with a unique memory location. - */ - void ensure_valid_merged_polygons () const; - - /** - * @brief Equality - */ - bool operator== (const db::Region &other) const; - - /** - * @brief Inequality - */ - bool operator!= (const db::Region &other) const - { - return !operator== (other); - } - - /** - * @brief Less operator - */ - bool operator< (const db::Region &other) const; - -private: - typedef db::layer polygon_layer_type; - typedef polygon_layer_type::iterator polygon_iterator_type; - - bool m_is_merged; - bool m_merged_semantics; - bool m_strict_handling; - bool m_merge_min_coherence; - mutable db::Shapes m_polygons; - mutable db::Shapes m_merged_polygons; - mutable db::Box m_bbox; - mutable bool m_bbox_valid; - mutable bool m_merged_polygons_valid; - mutable db::RecursiveShapeIterator m_iter; - db::ICplxTrans m_iter_trans; - bool m_report_progress; - std::string m_progress_desc; - - void init (); - void invalidate_cache (); - void set_valid_polygons (); - void ensure_bbox_valid () const; - void ensure_merged_polygons_valid () const; - EdgePairs run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - EdgePairs run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - void select_interacting_generic (const Region &other, int mode, bool touching, bool inverse); - Region selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; - Region selected_interacting_generic (const Edges &other, bool inverse) const; - void select_interacting_generic (const Edges &other, bool inverse); -}; -#endif - /** * @brief A polygon receiver putting the polygons into a Region object * diff --git a/src/db/db/dbRegionDelegate.cc b/src/db/db/dbRegionDelegate.cc new file mode 100644 index 000000000..42df393ad --- /dev/null +++ b/src/db/db/dbRegionDelegate.cc @@ -0,0 +1,91 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbRegionDelegate.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- + +RegionDelegate::RegionDelegate () +{ + m_report_progress = false; + m_merged_semantics = true; + m_strict_handling = false; + m_merge_min_coherence = false; +} + +RegionDelegate::RegionDelegate (const RegionDelegate &other) +{ + operator= (other); +} + +RegionDelegate & +RegionDelegate::operator= (const RegionDelegate &other) +{ + if (this != &other) { + m_report_progress = other.m_report_progress; + m_merged_semantics = other.m_merged_semantics; + m_strict_handling = other.m_strict_handling; + m_merge_min_coherence = other.m_merge_min_coherence; + } + return *this; +} + +RegionDelegate::~RegionDelegate () +{ + // .. nothing yet .. +} + +void RegionDelegate::enable_progress (const std::string &progress_desc) +{ + m_report_progress = true; + m_progress_desc = progress_desc; +} + +void RegionDelegate::disable_progress () +{ + m_report_progress = false; +} + +void RegionDelegate::set_min_coherence (bool f) +{ + m_merge_min_coherence = f; +} + +void RegionDelegate::set_merged_semantics (bool f) +{ + if (f != m_merged_semantics) { + m_merged_semantics = f; + merged_semantics_changed (); + } +} + +void RegionDelegate::set_strict_handling (bool f) +{ + m_strict_handling = f; +} + +} + diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h new file mode 100644 index 000000000..ecf10d292 --- /dev/null +++ b/src/db/db/dbRegionDelegate.h @@ -0,0 +1,206 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbRegionDelegate +#define HDR_dbRegionDelegate + +#include "dbCommon.h" + +#include "dbPolygon.h" +#include "dbEdges.h" +#include "dbEdgePairs.h" +#include "dbEdgePairRelations.h" + +#include + +namespace db { + +class RecursiveShapeIterator; +class EdgeFilterBase; +class PolygonFilterBase; + +/** + * @brief The region iterator delegate + */ +class DB_PUBLIC RegionIteratorDelegate +{ +public: + RegionIteratorDelegate () { } + virtual ~RegionIteratorDelegate () { } + + typedef db::Polygon value_type; + + virtual bool at_end () const = 0; + virtual void increment () = 0; + virtual const value_type *get () const = 0; + virtual RegionIteratorDelegate *clone () const = 0; +}; + +/** + * @brief The delegate for the actual region implementation + */ +class DB_PUBLIC RegionDelegate +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::Polygon polygon_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type distance_type; + typedef coord_traits::perimeter_type perimeter_type; + typedef coord_traits::area_type area_type; + + RegionDelegate (); + virtual ~RegionDelegate (); + + RegionDelegate (const RegionDelegate &other); + RegionDelegate &operator= (const RegionDelegate &other); + + virtual RegionDelegate *clone () const = 0; + + void enable_progress (const std::string &progress_desc); + void disable_progress (); + + void set_min_coherence (bool f); + bool min_coherence () const + { + return m_merge_min_coherence; + } + + void set_merged_semantics (bool f); + bool merged_semantics () const + { + return m_merged_semantics; + } + + void set_strict_handling (bool f); + bool strict_handling () const + { + return m_strict_handling; + } + + virtual std::string to_string (size_t nmax) const = 0; + + virtual RegionIteratorDelegate *begin () const = 0; + virtual RegionIteratorDelegate *begin_merged () const = 0; + + virtual std::pair begin_iter () const = 0; + virtual std::pair begin_merged_iter () const = 0; + + virtual bool empty () const = 0; + virtual bool is_box () const = 0; + virtual bool is_merged () const = 0; + virtual size_t size () const = 0; + + virtual area_type area (const db::Box &box) const = 0; + virtual perimeter_type perimeter (const db::Box &box) const = 0; + virtual Box bbox () const = 0; + + virtual EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const = 0; + virtual EdgePairs angle_check (double min, double max, bool inverse) const = 0; + + virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) = 0; + virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy) = 0; + + virtual Edges edges (const EdgeFilterBase *filter) const = 0; + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) = 0; + virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0; + + virtual RegionDelegate *merged_in_place () = 0; + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc) = 0; + virtual RegionDelegate *merged () const = 0; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const = 0; + + virtual RegionDelegate *strange_polygon_check () const = 0; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const = 0; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const = 0; + + virtual RegionDelegate *and_with (const Region &other) const = 0; + virtual RegionDelegate *not_with (const Region &other) const = 0; + virtual RegionDelegate *xor_with (const Region &other) const = 0; + virtual RegionDelegate *or_with (const Region &other) const = 0; + virtual RegionDelegate *add_in_place (const Region &other) = 0; + virtual RegionDelegate *add (const Region &other) const = 0; + + virtual RegionDelegate *selected_outside (const Region &other) const = 0; + virtual RegionDelegate *selected_not_outside (const Region &other) const = 0; + virtual RegionDelegate *selected_inside (const Region &other) const = 0; + virtual RegionDelegate *selected_not_inside (const Region &other) const = 0; + virtual RegionDelegate *selected_interacting (const Region &other) const = 0; + virtual RegionDelegate *selected_not_interacting (const Region &other) const = 0; + virtual RegionDelegate *selected_interacting (const Edges &other) const = 0; + virtual RegionDelegate *selected_not_interacting (const Edges &other) const = 0; + virtual RegionDelegate *selected_overlapping (const Region &other) const = 0; + virtual RegionDelegate *selected_not_overlapping (const Region &other) const = 0; + + virtual RegionDelegate *holes () const = 0; + virtual RegionDelegate *hulls () const = 0; + virtual RegionDelegate *in (const Region &other, bool invert) const = 0; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const = 0; + virtual RegionDelegate *smoothed (coord_type d) const = 0; + + virtual const db::Polygon *nth (size_t n) const = 0; + virtual bool has_valid_polygons () const = 0; + virtual bool has_valid_merged_polygons () const = 0; + + virtual const db::RecursiveShapeIterator *iter () const = 0; + + virtual bool equals (const Region &other) const = 0; + virtual bool less (const Region &other) const = 0; + +protected: + const std::string &progress_desc () const + { + return m_progress_desc; + } + + bool report_progress () const + { + return m_report_progress; + } + + virtual void merged_semantics_changed () { } + +private: + bool m_merged_semantics; + bool m_strict_handling; + bool m_merge_min_coherence; + bool m_report_progress; + std::string m_progress_desc; +}; + +} + +#endif + diff --git a/src/db/unit_tests/dbTilingProcessor.cc b/src/db/unit_tests/dbTilingProcessor.cc index 5aa0b1216..c352cb19a 100644 --- a/src/db/unit_tests/dbTilingProcessor.cc +++ b/src/db/unit_tests/dbTilingProcessor.cc @@ -29,6 +29,7 @@ #include "gsiDecl.h" #include "dbWriter.h" #include "dbSaveLayoutOptions.h" +#include "dbShapeProcessor.h" #include #include From 009492a2a616902e34992696f4f5d3738ef53b4e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 7 Nov 2018 22:19:05 +0100 Subject: [PATCH 041/335] Fixed a linker issue. --- src/db/db/dbRegion.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 9dfff7aca..a3161329d 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -111,8 +111,8 @@ Region &Region::transform (const T &trans) } // explicit instantiations -template <> Region &Region::transform (const db::ICplxTrans &); -template <> Region &Region::transform (const db::Trans &); +template Region &Region::transform (const db::ICplxTrans &); +template Region &Region::transform (const db::Trans &); template void Region::insert (const Sh &shape) From de6045fdf0f39868e68489b887bca8c60efed5ea Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 7 Nov 2018 22:20:57 +0100 Subject: [PATCH 042/335] Fixed an initialization issue. --- src/db/db/dbRegion.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index a8b08eee0..e91ad215c 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -507,6 +507,7 @@ public: */ template Region (const Sh &s) + : mp_delegate (0) { insert (s); } @@ -520,6 +521,7 @@ public: */ template Region (const Iter &b, const Iter &e) + : mp_delegate (0) { reserve (e - b); for (Iter i = b; i != e; ++i) { From 0a9ab32f81f81b55fbac59354a3a70dea6092998 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 7 Nov 2018 23:08:52 +0100 Subject: [PATCH 043/335] WIP: fixed initialization code. --- src/db/db/db.pro | 6 +- src/db/db/dbEdgesDelegate.cc | 1376 ++++++++++++++++++++++++++++++++++ src/db/db/dbEdgesDelegate.h | 1253 +++++++++++++++++++++++++++++++ src/db/db/dbRegion.cc | 6 +- 4 files changed, 2637 insertions(+), 4 deletions(-) create mode 100644 src/db/db/dbEdgesDelegate.cc create mode 100644 src/db/db/dbEdgesDelegate.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 788335b11..8c6cf60a5 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -124,7 +124,8 @@ SOURCES = \ dbEmptyRegion.cc \ dbFlatRegion.cc \ dbOriginalLayerRegion.cc \ - dbRegionDelegate.cc + dbRegionDelegate.cc \ + dbEdgesDelegate.cc HEADERS = \ dbArray.h \ @@ -218,7 +219,8 @@ HEADERS = \ dbEmptyRegion.h \ dbFlatRegion.h \ dbOriginalLayerRegion.h \ - dbRegionDelegate.h + dbRegionDelegate.h \ + dbEdgesDelegate.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbEdgesDelegate.cc b/src/db/db/dbEdgesDelegate.cc new file mode 100644 index 000000000..b64ee6a76 --- /dev/null +++ b/src/db/db/dbEdgesDelegate.cc @@ -0,0 +1,1376 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 0 + +#include "dbEdges.h" +#include "dbEdgePairs.h" +#include "dbRegion.h" +#include "dbLayoutUtils.h" +#include "dbBoxConvert.h" +#include "dbBoxScanner.h" +#include "dbPolygonTools.h" +#include "dbShapeProcessor.h" + +#include "tlIntervalMap.h" +#include "tlVariant.h" + +#include + +namespace db +{ + +Edges::Edges (const RecursiveShapeIterator &si, bool as_edges) + : m_edges (false), m_merged_edges (false) +{ + init (); + if (! as_edges) { + m_iter = si; + } else { + for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { + insert (s.shape (), s.trans ()); + } + } + m_bbox_valid = false; + m_is_merged = false; +} + +Edges::Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges, bool merged_semantics) + : m_edges (false), m_merged_edges (false) +{ + init (); + if (! as_edges) { + m_iter = si; + m_iter_trans = trans; + } else { + for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { + insert (s.shape (), trans * s.trans ()); + } + } + m_bbox_valid = false; + m_is_merged = false; + m_merged_semantics = merged_semantics; +} + +bool +Edges::operator== (const db::Edges &other) const +{ + if (empty () != other.empty ()) { + return false; + } + if (size () != other.size ()) { + return false; + } + db::Edges::const_iterator o1 = begin (); + db::Edges::const_iterator o2 = other.begin (); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return false; + } + ++o1; + ++o2; + } + return true; +} + +bool +Edges::operator< (const db::Edges &other) const +{ + if (empty () != other.empty ()) { + return empty () < other.empty (); + } + if (size () != other.size ()) { + return (size () < other.size ()); + } + db::Edges::const_iterator o1 = begin (); + db::Edges::const_iterator o2 = other.begin (); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return *o1 < *o2; + } + ++o1; + ++o2; + } + return false; +} + +std::string +Edges::to_string (size_t nmax) const +{ + std::ostringstream os; + const_iterator e = begin (); + bool first = true; + for ( ; ! e.at_end () && nmax != 0; ++e, --nmax) { + if (! first) { + os << ";"; + } + first = false; + os << e->to_string (); + } + if (! e.at_end ()) { + os << "..."; + } + return os.str (); +} + +void +Edges::swap (Edges &other) +{ + std::swap (m_is_merged, other.m_is_merged); + std::swap (m_merged_semantics, other.m_merged_semantics); + m_edges.swap (other.m_edges); + m_merged_edges.swap (other.m_merged_edges); + std::swap (m_bbox, other.m_bbox); + std::swap (m_bbox_valid, other.m_bbox_valid); + std::swap (m_merged_edges_valid, other.m_merged_edges_valid); + std::swap (m_iter, other.m_iter); + std::swap (m_iter_trans, other.m_iter_trans); +} + +void +Edges::invalidate_cache () +{ + m_bbox_valid = false; + m_merged_edges.clear (); + m_merged_edges_valid = false; +} + +void +Edges::disable_progress () +{ + m_report_progress = false; +} + +void +Edges::enable_progress (const std::string &progress_desc) +{ + m_report_progress = true; + m_progress_desc = progress_desc; +} + +Edges::distance_type +Edges::length (const db::Box &box) const +{ + distance_type l = 0; + + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + + if (box.empty () || (box.contains (e->p1 ()) && box.contains (e->p2 ()))) { + l += e->length (); + } else { + + std::pair ce = e->clipped (box); + if (ce.first) { + + db::Coord dx = ce.second.dx (); + db::Coord dy = ce.second.dy (); + db::Coord x = ce.second.p1 ().x (); + db::Coord y = ce.second.p1 ().y (); + if ((dx == 0 && x == box.left () && dy < 0) || + (dx == 0 && x == box.right () && dy > 0) || + (dy == 0 && y == box.top () && dx < 0) || + (dy == 0 && y == box.bottom () && dx > 0)) { + // not counted -> box is at outside side of the edge + } else { + l += ce.second.length (); + } + + } + + } + + } + + return l; +} + +Edges +Edges::start_segments (db::Edges::length_type length, double fraction) const +{ + Edges edges; + edges.reserve (m_edges.size ()); + + // zero-length edges would vanish in merged sematics, so we don't set it now + if (length == 0) { + edges.set_merged_semantics (false); + } + + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + double l = std::max (e->double_length () * fraction, double (length)); + edges.insert (db::Edge (e->p1 (), db::Point (db::DPoint (e->p1 ()) + db::DVector (e->d()) * (l / e->double_length ())))); + } + + return edges; +} + +Edges +Edges::end_segments (db::Edges::length_type length, double fraction) const +{ + Edges edges; + edges.reserve (m_edges.size ()); + + // zero-length edges would vanish in merged sematics, so we don't set it now + if (length == 0) { + edges.set_merged_semantics (false); + } + + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + double l = std::max (e->double_length () * fraction, double (length)); + edges.insert (db::Edge (db::Point (db::DPoint (e->p2 ()) - db::DVector (e->d()) * (l / e->double_length ())), e->p2 ())); + } + + return edges; +} + +Edges +Edges::centers (db::Edges::length_type length, double fraction) const +{ + Edges edges; + edges.reserve (m_edges.size ()); + + // zero-length edges would vanish in merged sematics, so we don't set it now + if (length == 0) { + edges.set_merged_semantics (false); + } + + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + double l = std::max (e->double_length () * fraction, double (length)); + db::DVector dl = db::DVector (e->d()) * (0.5 * l / e->double_length ()); + db::DPoint center = db::DPoint (e->p1 ()) + db::DVector (e->p2 () - e->p1 ()) * 0.5; + edges.insert (db::Edge (db::Point (center - dl), db::Point (center + dl))); + } + + return edges; +} + +void +Edges::edge_region_op (const Region &other, bool outside, bool include_borders) +{ + // shortcuts + if (other.empty ()) { + if (! outside) { + clear (); + } + return; + } else if (empty ()) { + return; + } + + db::EdgeProcessor ep (m_report_progress, m_progress_desc); + + for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { + if (p->box ().touches (bbox ())) { + ep.insert (*p, 0); + } + } + + for (const_iterator e = begin (); ! e.at_end (); ++e) { + ep.insert (*e, 1); + } + + invalidate_cache (); + + db::EdgeShapeGenerator cc (m_edges, true /*clear*/); + db::EdgePolygonOp op (outside, include_borders); + ep.process (cc, op); + + set_valid_edges (); + + m_is_merged = false; +} + +namespace +{ + +struct OrJoinOp +{ + void operator() (int &v, int n) + { + v += n; + } +}; + +struct AndJoinOp +{ + void operator() (int &v, int n) + { + if (n == 0) { + v = 0; + } + } +}; + +struct NotJoinOp +{ + void operator() (int &v, int n) + { + if (n != 0) { + v = 0; + } + } +}; + +struct XorJoinOp +{ + void operator() (int &v, int n) + { + if (n != 0) { + if (v == 0) { + v = (n > 0 ? 1 : -1); + } else { + v = 0; + } + } + } +}; + +struct EdgeBooleanCluster + : public db::cluster +{ + typedef db::Edge::coord_type coord_type; + + EdgeBooleanCluster (db::Edges *output, db::Edges::BoolOp op) + : mp_output (output), m_op (op) + { + // .. nothing yet .. + } + + void finish () + { + // determine base edge (longest overall edge) + + // shortcut for single edge + if (begin () + 1 == end ()) { + if (begin ()->second == 0) { + if (m_op != db::Edges::And) { + mp_output->insert (*(begin ()->first)); + } + } else { + if (m_op != db::Edges::And && m_op != db::Edges::Not) { + mp_output->insert (*(begin ()->first)); + } + } + return; + } + + db::Edge r = *begin ()->first; + double l1 = 0.0, l2 = r.double_length (); + double n = 1.0 / l2; + db::Point p1 = r.p1 (), p2 = r.p2 (); + + for (iterator o = begin () + 1; o != end (); ++o) { + double ll1 = db::sprod (db::Vector (o->first->p1 () - r.p1 ()), r.d ()) * n; + double ll2 = db::sprod (db::Vector (o->first->p2 () - r.p1 ()), r.d ()) * n; + if (ll1 < l1) { + p1 = o->first->p1 (); + l1 = ll1; + } + if (ll2 < l1) { + p1 = o->first->p2 (); + l1 = ll2; + } + if (ll1 > l2) { + p2 = o->first->p1 (); + l2 = ll1; + } + if (ll2 > l2) { + p2 = o->first->p2 (); + l2 = ll2; + } + } + + db::Vector d = db::Vector (p2 - p1); + n = 1.0 / d.double_length (); + + OrJoinOp or_jop; + AndJoinOp and_jop; + NotJoinOp not_jop; + XorJoinOp xor_jop; + + tl::interval_map a, b; + a.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); + b.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); + + for (iterator o = begin (); o != end (); ++o) { + db::Coord l1 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p1 () - p1), d) * n); + db::Coord l2 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p2 () - p1), d) * n); + if (o->second == 0 || m_op == db::Edges::Or) { + if (l1 < l2) { + a.add (l1, l2, 1, or_jop); + } else if (l1 > l2) { + a.add (l2, l1, -1, or_jop); + } + } else { + if (l1 < l2) { + b.add (l1, l2, 1, or_jop); + } else { + b.add (l2, l1, -1, or_jop); + } + } + } + + tl::interval_map q; + for (tl::interval_map::const_iterator ia = a.begin (); ia != a.end (); ++ia) { + q.add (ia->first.first, ia->first.second, ia->second > 0 ? 1 : (ia->second < 0 ? -1 : 0), or_jop); + } + + if (b.begin () == b.end ()) { + + // optimize for empty b + if (m_op != db::Edges::And) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + if (ib->second > 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.first * n)), p1 + db::Vector (d * (ib->first.second * n)))); + } else if (ib->second < 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.second * n)), p1 + db::Vector (d * (ib->first.first * n)))); + } + } + } + + } else { + + if (m_op == db::Edges::And) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + q.add (ib->first.first, ib->first.second, ib->second, and_jop); + } + } else if (m_op == db::Edges::Not) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + q.add (ib->first.first, ib->first.second, ib->second, not_jop); + } + } else if (m_op == db::Edges::Xor) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + q.add (ib->first.first, ib->first.second, ib->second, xor_jop); + } + } + + for (tl::interval_map::const_iterator iq = q.begin (); iq != q.end (); ++iq) { + if (iq->second > 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.first * n)), p1 + db::Vector (d * (iq->first.second * n)))); + } else if (iq->second < 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.second * n)), p1 + db::Vector (d * (iq->first.first * n)))); + } + } + + } + + } + +private: + db::Edges *mp_output; + db::Edges::BoolOp m_op; +}; + +struct EdgeBooleanClusterCollector + : public db::cluster_collector +{ + EdgeBooleanClusterCollector (db::Edges *output, Edges::BoolOp op) + : db::cluster_collector (EdgeBooleanCluster (output, op), op != Edges::And /*report single*/) + { + // .. nothing yet .. + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + // Select edges which are: + // 1.) not degenerate + // 2.) parallel with some tolerance of roughly 1 dbu + // 3.) connected + if (! o1->is_degenerate () && ! o2->is_degenerate () + && fabs ((double) db::vprod (*o1, *o2)) < db::coord_traits::prec_distance () * std::min (o1->double_length (), o2->double_length ()) + && (o1->p1 () == o2->p1 () || o1->p1 () == o2->p2 () || o1->p2 () == o2->p1 () || o1->p2 () == o2->p2 () || o1->coincident (*o2))) { + db::cluster_collector::add (o1, p1, o2, p2); + } + } +}; + +} + +void +Edges::inplace_boolean (const Edges *other, Edges::BoolOp op) +{ + Edges out = boolean (other, op); + swap (out); +} + +Edges +Edges::boolean (const Edges *other, Edges::BoolOp op) const +{ + Edges output; + EdgeBooleanClusterCollector cluster_collector (&output, op); + + db::box_scanner scanner (m_report_progress, m_progress_desc); + scanner.reserve (m_edges.size () + (other ? other->size () : 0)); + + ensure_valid_edges (); + for (const_iterator e = begin (); ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (&*e, 0); + } + } + if (other) { + other->ensure_valid_edges (); + for (const_iterator e = other->begin (); ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (&*e, 1); + } + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + output.m_is_merged = true; + return output; +} + +void +Edges::ensure_valid_merged_edges () const +{ + // If no merged semantics applies or we will deliver the original + // edges as merged ones, we need to make sure those are valid + // ones (with a unique memory address) + if (! m_merged_semantics || m_is_merged) { + ensure_valid_edges (); + } else { + ensure_merged_edges_valid (); + } +} + +void +Edges::ensure_merged_edges_valid () const +{ + if (! m_merged_edges_valid) { + + m_merged_edges.clear (); + + Edges tmp; + EdgeBooleanClusterCollector cluster_collector (&tmp, Or); + + db::box_scanner scanner (m_report_progress, m_progress_desc); + scanner.reserve (m_edges.size ()); + + ensure_valid_edges (); + for (const_iterator e = begin (); ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (&*e, 0); + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + m_merged_edges.swap (tmp.m_edges); + m_merged_edges_valid = true; + + } +} + +Edges & +Edges::operator+= (const Edges &other) +{ + invalidate_cache (); + + if (! has_valid_edges ()) { + + m_edges.clear (); + + size_t n = 0; + for (const_iterator e = begin (); ! e.at_end (); ++e) { + ++n; + } + for (const_iterator e = other.begin (); ! e.at_end (); ++e) { + ++n; + } + + m_edges.reserve (db::Edge::tag (), n); + + for (const_iterator e = begin (); ! e.at_end (); ++e) { + m_edges.insert (*e); + } + for (const_iterator e = other.begin (); ! e.at_end (); ++e) { + m_edges.insert (*e); + } + + set_valid_edges (); + + } else if (! other.has_valid_edges ()) { + + size_t n = m_edges.size (); + for (const_iterator e = other.begin (); ! e.at_end (); ++e) { + ++n; + } + + m_edges.reserve (db::Edge::tag (), n); + + for (const_iterator e = other.begin (); ! e.at_end (); ++e) { + m_edges.insert (*e); + } + + } else { + m_edges.insert (other.m_edges.get_layer ().begin (), other.m_edges.get_layer ().end ()); + } + + m_is_merged = false; + return *this; +} + +namespace +{ + +/** + * @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver + * + * Note: This special scanner uses pointers to two different objects: edges and polygons. + * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate + * pointers to edges. + * + * There is a special box converter which is able to sort that out as well. + */ +template +class edge_to_region_interaction_filter + : public db::box_scanner_receiver +{ +public: + edge_to_region_interaction_filter (OutputContainer &output) + : mp_output (&output) + { + // .. nothing yet .. + } + + void add (const char *o1, size_t p1, const char *o2, size_t p2) + { + const db::Edge *e = 0; + const db::Polygon *p = 0; + + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + if (p1 == 0 && p2 == 1) { + e = reinterpret_cast (o1); + p = reinterpret_cast (o2 - 1); + } else if (p1 == 1 && p2 == 0) { + e = reinterpret_cast (o2); + p = reinterpret_cast (o1 - 1); + } + + if (e && p && m_seen.find (e) == m_seen.end ()) { + if (db::interact (*p, *e)) { + m_seen.insert (e); + mp_output->insert (*e); + } + } + } + +private: + OutputContainer *mp_output; + std::set m_seen; +}; + +/** + * @brief A special box converter that splits the pointers into polygon and edge pointers + */ +struct EdgeOrRegionBoxConverter +{ + typedef db::Box box_type; + + db::Box operator() (const char &c) const + { + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + const char *cp = &c; + if ((size_t (cp) & 1) == 1) { + // it's a polygon + return (reinterpret_cast (cp - 1))->box (); + } else { + // it's an edge + const db::Edge *e = reinterpret_cast (cp); + return db::Box (e->p1 (), e->p2 ()); + } + } +}; + +} + +Edges & +Edges::select_interacting (const Region &other) +{ + // shortcuts + if (other.empty ()) { + clear (); + return *this; + } else if (empty ()) { + return *this; + } + + db::box_scanner scanner (m_report_progress, m_progress_desc); + scanner.reserve (size () + other.size ()); + + ensure_valid_merged_edges (); + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + scanner.insert ((char *) &*e, 0); + } + + AddressablePolygonDelivery p = other.addressable_polygons (); + + for ( ; ! p.at_end (); ++p) { + // NOTE: dirty hack - we can take the address of the polygon because we used ensure_flat_polygons. + scanner.insert ((char *) p.operator-> () + 1, 1); + } + + Edges output; + edge_to_region_interaction_filter filter (output); + EdgeOrRegionBoxConverter bc; + scanner.process (filter, 1, bc); + + swap (output); + return *this; +} + +Edges & +Edges::select_not_interacting (const Region &other) +{ + // shortcuts + if (other.empty () || empty ()) { + return *this; + } + + db::box_scanner scanner (m_report_progress, m_progress_desc); + scanner.reserve (size () + other.size ()); + + ensure_valid_merged_edges (); + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + scanner.insert ((char *) &*e, 0); + } + + AddressablePolygonDelivery p = other.addressable_polygons (); + for ( ; ! p.at_end (); ++p) { + // NOTE: dirty hack - we can take the address of the polygon because we used ensure_flat_polygons. + scanner.insert ((char *) p.operator-> () + 1, 1); + } + + std::set interacting; + edge_to_region_interaction_filter > filter (interacting); + EdgeOrRegionBoxConverter bc; + scanner.process (filter, 1, bc); + + Edges output; + for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { + if (interacting.find (*o) == interacting.end ()) { + output.insert (*o); + } + } + + swap (output); + return *this; +} + +namespace +{ + +/** + * @brief A helper class for the edge interaction functionality which acts as an edge pair receiver + */ +template +class edge_interaction_filter + : public db::box_scanner_receiver +{ +public: + edge_interaction_filter (OutputContainer &output) + : mp_output (&output) + { + // .. nothing yet .. + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + // Select the edges which intersect + if (p1 != p2) { + const db::Edge *o = p1 > p2 ? o2 : o1; + const db::Edge *oo = p1 > p2 ? o1 : o2; + if (o->intersect (*oo)) { + if (m_seen.insert (o).second) { + mp_output->insert (*o); + } + } + } + } + +private: + OutputContainer *mp_output; + std::set m_seen; +}; + +} + +Edges & +Edges::select_interacting (const Edges &other) +{ + db::box_scanner scanner (m_report_progress, m_progress_desc); + scanner.reserve (size () + other.size ()); + + ensure_valid_merged_edges (); + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + scanner.insert (&*e, 0); + } + other.ensure_valid_edges (); + for (const_iterator e = other.begin (); ! e.at_end (); ++e) { + scanner.insert (&*e, 1); + } + + Edges output; + edge_interaction_filter filter (output); + scanner.process (filter, 1, db::box_convert ()); + + swap (output); + return *this; +} + +Edges & +Edges::select_not_interacting (const Edges &other) +{ + db::box_scanner scanner (m_report_progress, m_progress_desc); + scanner.reserve (size () + other.size ()); + + ensure_valid_merged_edges (); + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + scanner.insert (&*e, 0); + } + other.ensure_valid_edges (); + for (const_iterator e = other.begin (); ! e.at_end (); ++e) { + scanner.insert (&*e, 1); + } + + std::set interacting; + edge_interaction_filter > filter (interacting); + scanner.process (filter, 1, db::box_convert ()); + + Edges output; + for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { + if (interacting.find (*o) == interacting.end ()) { + output.insert (*o); + } + } + + swap (output); + return *this; +} + +namespace +{ + +struct JoinEdgesCluster + : public db::cluster +{ + typedef db::Edge::coord_type coord_type; + + JoinEdgesCluster (db::Region *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) + : mp_output (output), m_ext_b (ext_b), m_ext_e (ext_e), m_ext_o (ext_o), m_ext_i (ext_i) + { + // .. nothing yet .. + } + + void finish () + { + std::multimap objects_by_p1; + std::multimap objects_by_p2; + for (iterator o = begin (); o != end (); ++o) { + if (o->first->p1 () != o->first->p2 ()) { + objects_by_p1.insert (std::make_pair (o->first->p1 (), o)); + objects_by_p2.insert (std::make_pair (o->first->p2 (), o)); + } + } + + while (! objects_by_p2.empty ()) { + + tl_assert (! objects_by_p1.empty ()); + + // Find the beginning of a new sequence + std::multimap::iterator j0 = objects_by_p1.begin (); + std::multimap::iterator j = j0; + do { + std::multimap::iterator jj = objects_by_p2.find (j->first); + if (jj == objects_by_p2.end ()) { + break; + } else { + j = objects_by_p1.find (jj->second->first->p1 ()); + tl_assert (j != objects_by_p1.end ()); + } + } while (j != j0); + + iterator i = j->second; + + // determine a sequence + // TODO: this chooses any solution in case of forks. Choose a specific one? + std::vector pts; + pts.push_back (i->first->p1 ()); + + do { + + // record the next point + pts.push_back (i->first->p2 ()); + + // remove the edge as it's taken + std::multimap::iterator jj; + for (jj = objects_by_p2.find (i->first->p2 ()); jj != objects_by_p2.end () && jj->first == i->first->p2 (); ++jj) { + if (jj->second == i) { + break; + } + } + tl_assert (jj != objects_by_p2.end () && jj->second == i); + objects_by_p2.erase (jj); + objects_by_p1.erase (j); + + // process along the edge to the next one + // TODO: this chooses any solution in case of forks. Choose a specific one? + j = objects_by_p1.find (i->first->p2 ()); + if (j != objects_by_p1.end ()) { + i = j->second; + } else { + break; + } + + } while (true); + + bool cyclic = (pts.back () == pts.front ()); + + if (! cyclic) { + + // non-cyclic sequence + db::Path path (pts.begin (), pts.end (), 0, m_ext_b, m_ext_e, false); + std::vector hull; + path.hull (hull, m_ext_o, m_ext_i); + db::Polygon poly; + poly.assign_hull (hull.begin (), hull.end ()); + mp_output->insert (poly); + + } else { + + // we have a loop: form a contour by using the polygon size functions and a "Not" to form the hole + db::Polygon poly; + poly.assign_hull (pts.begin (), pts.end ()); + + db::EdgeProcessor ep; + db::RegionPolygonSink ps (*mp_output, false); + db::PolygonGenerator pg (ps, false, true); + + int mode_a = -1, mode_b = -1; + + if (m_ext_o == 0) { + ep.insert (poly, 0); + } else { + db::Polygon sized_poly (poly); + sized_poly.size (m_ext_o, m_ext_o, 2 /*sizing mode*/); + ep.insert (sized_poly, 0); + mode_a = 1; + } + + if (m_ext_i == 0) { + ep.insert (poly, 1); + } else { + db::Polygon sized_poly (poly); + sized_poly.size (-m_ext_i, -m_ext_i, 2 /*sizing mode*/); + ep.insert (sized_poly, 1); + mode_b = 1; + } + + db::BooleanOp2 op (db::BooleanOp::ANotB, mode_a, mode_b); + ep.process (pg, op); + + } + + } + } + +private: + db::Region *mp_output; + coord_type m_ext_b, m_ext_e, m_ext_o, m_ext_i; +}; + +struct JoinEdgesClusterCollector + : public db::cluster_collector +{ + typedef db::Edge::coord_type coord_type; + + JoinEdgesClusterCollector (db::Region *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) + : db::cluster_collector (JoinEdgesCluster (output, ext_b, ext_e, ext_o, ext_i), true) + { + // .. nothing yet .. + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + if (o1->p2 () == o2->p1 () || o1->p1 () == o2->p2 ()) { + db::cluster_collector::add (o1, p1, o2, p2); + } + } +}; + +} + +void +Edges::extended (Region &output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const +{ + if (join) { + + JoinEdgesClusterCollector cluster_collector (&output, ext_b, ext_e, ext_o, ext_i); + + db::box_scanner scanner (m_report_progress, m_progress_desc); + scanner.reserve (size ()); + + ensure_valid_edges (); + size_t n = 0; + for (const_iterator e = begin (); ! e.at_end (); ++e) { + scanner.insert (&*e, n); + ++n; + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + } else { + + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + + db::DVector d; + if (e->is_degenerate ()) { + d = db::DVector (1.0, 0.0); + } else { + d = db::DVector (e->d ()) * (1.0 / e->double_length ()); + } + + db::DVector n (-d.y (), d.x ()); + + db::Point pts[4] = { + db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) + n * double (ext_o)), + db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) + n * double (ext_o)), + db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) - n * double (ext_i)), + db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) - n * double (ext_i)), + }; + + db::Polygon poly; + poly.assign_hull (pts + 0, pts + 4); + output.insert (poly); + + } + + } +} + +Edges +Edges::in (const Edges &other, bool invert) const +{ + std::set op; + for (const_iterator o = other.begin_merged (); ! o.at_end (); ++o) { + op.insert (*o); + } + + Edges r; + for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { + if ((op.find (*o) == op.end ()) == invert) { + r.insert (*o); + } + } + + return r; +} + +void +Edges::init () +{ + m_report_progress = false; + m_bbox_valid = true; + m_is_merged = true; + m_merged_semantics = true; + m_merged_edges_valid = false; +} + +void +Edges::ensure_bbox_valid () const +{ + if (! m_bbox_valid) { + m_bbox = db::Box (); + for (const_iterator e = begin (); ! e.at_end (); ++e) { + m_bbox += db::Box (e->p1 (), e->p2 ()); + } + m_bbox_valid = true; + } +} + +Edges::const_iterator +Edges::begin_merged () const +{ + if (! m_merged_semantics || m_is_merged) { + return begin (); + } else { + ensure_merged_edges_valid (); + return db::EdgesIterator (m_merged_edges.get_layer ().begin (), m_merged_edges.get_layer ().end ()); + } +} + +std::pair +Edges::begin_iter () const +{ + if (has_valid_edges ()) { + return std::make_pair (db::RecursiveShapeIterator (m_edges), db::ICplxTrans ()); + } else { + return std::make_pair (m_iter, m_iter_trans); + } +} + +std::pair +Edges::begin_merged_iter () const +{ + if (! m_merged_semantics || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_edges_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_edges), db::ICplxTrans ()); + } +} + +void +Edges::insert (const db::Edge &edge) +{ + ensure_valid_edges (); + m_edges.insert (edge); + m_is_merged = false; + invalidate_cache (); +} + +void +Edges::insert (const db::Box &box) +{ + if (! box.empty ()) { + ensure_valid_edges (); + m_edges.insert (db::Edge (box.lower_left (), box.upper_left ())); + m_edges.insert (db::Edge (box.upper_left (), box.upper_right ())); + m_edges.insert (db::Edge (box.upper_right (), box.lower_right ())); + m_edges.insert (db::Edge (box.lower_right (), box.lower_left ())); + m_is_merged = false; + invalidate_cache (); + } +} + +void +Edges::insert (const db::Path &path) +{ + if (path.points () > 0) { + insert (path.polygon ()); + } +} + +void +Edges::insert (const db::Polygon &polygon) +{ + ensure_valid_edges (); + for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + m_edges.insert (*e); + } + m_is_merged = false; + invalidate_cache (); +} + +void +Edges::insert (const db::SimplePolygon &polygon) +{ + ensure_valid_edges (); + for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + m_edges.insert (*e); + } + m_is_merged = false; + invalidate_cache (); +} + +void +Edges::clear () +{ + m_edges.clear (); + m_bbox = db::Box (); + m_bbox_valid = true; + m_is_merged = true; + m_merged_edges.clear (); + m_merged_edges_valid = true; + m_iter = db::RecursiveShapeIterator (); + m_iter_trans = db::ICplxTrans (); +} + +namespace +{ + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + * + * If will perform a edge by edge check using the provided EdgeRelationFilter + */ +class Edge2EdgeCheck + : public db::box_scanner_receiver +{ +public: + Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool requires_different_layers) + : mp_check (&check), mp_output (&output) + { + m_requires_different_layers = requires_different_layers; + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + // Overlap or inside checks require input from different layers + if (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0) { + + // ensure that the first check argument is of layer 1 and the second of + // layer 2 (unless both are of the same layer) + int l1 = int (p1 & size_t (1)); + int l2 = int (p2 & size_t (1)); + + db::EdgePair ep; + if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { + mp_output->insert (ep); + } + + } + } + +private: + const EdgeRelationFilter *mp_check; + EdgePairs *mp_output; + bool m_requires_different_layers; +}; + +} + +EdgePairs +Edges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + EdgePairs result; + + db::box_scanner scanner (m_report_progress, m_progress_desc); + scanner.reserve (size () + (other ? other->size () : 0)); + + ensure_valid_merged_edges (); + size_t n = 0; + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + scanner.insert (&*e, n); + n += 2; + } + + if (other) { + other->ensure_valid_merged_edges (); + n = 1; + for (const_iterator e = other->begin_merged (); ! e.at_end (); ++e) { + scanner.insert (&*e, n); + n += 2; + } + } + + EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (other != 0); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + Edge2EdgeCheck edge_check (check, result, other != 0); + scanner.process (edge_check, d, db::box_convert ()); + + return result; +} + +size_t +Edges::size () const +{ + if (! has_valid_edges ()) { + // If we have an iterator, we have to do it the hard way .. + size_t n = 0; + for (const_iterator e = begin (); ! e.at_end (); ++e) { + ++n; + } + return n; + } else { + return m_edges.size (); + } +} + +void +Edges::set_merged_semantics (bool f) +{ + if (f != m_merged_semantics) { + m_merged_semantics = f; + m_merged_edges.clear (); + m_merged_edges_valid = false; + } +} + +void +Edges::set_valid_edges () +{ + m_iter = db::RecursiveShapeIterator (); +} + +void +Edges::ensure_valid_edges () const +{ + if (! has_valid_edges ()) { + + m_edges.clear (); + + size_t n = 0; + for (const_iterator e = begin (); ! e.at_end (); ++e) { + ++n; + } + m_edges.reserve (db::Edge::tag (), n); + + for (const_iterator e = begin (); ! e.at_end (); ++e) { + m_edges.insert (*e); + } + + m_iter = db::RecursiveShapeIterator (); + + } +} + +} // namespace db + +namespace tl +{ + template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::Edges &b) + { + db::Edge e; + + if (! ex.try_read (e)) { + return false; + } + b.insert (e); + + while (ex.test (";")) { + ex.read (e); + b.insert (e); + } + + return true; + } + + template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::Edges &b) + { + if (! test_extractor_impl (ex, b)) { + ex.error (tl::to_string (tr ("Expected an edge collection specification"))); + } + } +} + +#endif + diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h new file mode 100644 index 000000000..7fefbe4a8 --- /dev/null +++ b/src/db/db/dbEdgesDelegate.h @@ -0,0 +1,1253 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 0 + +#ifndef HDR_dbEdges +#define HDR_dbEdges + +#include "dbCommon.h" + +#include "dbTypes.h" +#include "dbEdge.h" +#include "dbTrans.h" +#include "dbShape.h" +#include "dbShapes.h" +#include "dbShapes2.h" +#include "dbEdgePairRelations.h" +#include "dbEdgePairs.h" +#include "dbRecursiveShapeIterator.h" +#include "tlString.h" + +namespace db { + +class Edges; + +/** + * @brief A base class for polygon filters + */ +class DB_PUBLIC EdgeFilterBase +{ +public: + EdgeFilterBase () { } + virtual ~EdgeFilterBase () { } + + virtual bool selected (const db::Edge &edge) const = 0; +}; + +/** + * @brief An edge length filter for use with Edges::filter or Edges::filtered + * + * This filter has two parameters: lmin and lmax. + * It will filter all edges for which the length is >= lmin and < lmax. + * There is an "invert" flag which allows to select all edges not + * matching the criterion. + */ + +struct DB_PUBLIC EdgeLengthFilter + : public EdgeFilterBase +{ + typedef db::Edge::distance_type length_type; + + /** + * @brief Constructor + * + * @param lmin The minimum length + * @param lmax The maximum length + * @param inverse If set to true, only polygons not matching this criterion will be filtered + */ + EdgeLengthFilter (length_type lmin, length_type lmax, bool inverse) + : m_lmin (lmin), m_lmax (lmax), m_inverse (inverse) + { + // .. nothing yet .. + } + + /** + * @brief Returns true if the edge length matches the criterion + */ + bool selected (const db::Edge &edge) const + { + length_type l = edge.length (); + if (! m_inverse) { + return l >= m_lmin && l < m_lmax; + } else { + return ! (l >= m_lmin && l < m_lmax); + } + } + +private: + length_type m_lmin, m_lmax; + bool m_inverse; +}; + +/** + * @brief An edge orientation filter for use with Edges::filter or Edges::filtered + * + * This filter has two parameters: amin and amax. + * It will filter all edges for which the orientation angle is >= amin and < amax. + * The orientation angle is measured in degree against the x axis in the mathematical sense. + * There is an "invert" flag which allows to select all edges not + * matching the criterion. + */ + +struct DB_PUBLIC EdgeOrientationFilter + : public EdgeFilterBase +{ + /** + * @brief Constructor + * + * @param amin The minimum angle (measured against the x axis) + * @param amax The maximum angle (measured against the x axis) + * @param inverse If set to true, only polygons not matching this criterion will be filtered + * + * This filter will filter out all edges whose angle against x axis + * is larger or equal to amin and less than amax. + */ + EdgeOrientationFilter (double amin, double amax, bool inverse) + : m_inverse (inverse), m_exact (false) + { + m_emin = db::DVector (cos (amin * M_PI / 180.0), sin (amin * M_PI / 180.0)); + m_emax = db::DVector (cos (amax * M_PI / 180.0), sin (amax * M_PI / 180.0)); + } + + /** + * @brief Constructor + * + * @param a The angle (measured against the x axis) + * @param inverse If set to true, only polygons not matching this criterion will be filtered + * + * This filter will filter out all edges whose angle against x axis + * is equal to a. + */ + EdgeOrientationFilter (double a, bool inverse) + : m_inverse (inverse), m_exact (true) + { + m_emin = db::DVector (cos (a * M_PI / 180.0), sin (a * M_PI / 180.0)); + } + + /** + * @brief Returns true if the edge orientation matches the criterion + */ + bool selected (const db::Edge &edge) const + { + int smin = db::vprod_sign (m_emin, db::DVector (edge.d ())); + if (m_exact) { + if (! m_inverse) { + return smin == 0; + } else { + return smin != 0; + } + } else { + int smax = db::vprod_sign (m_emax, db::DVector (edge.d ())); + if (! m_inverse) { + return (smin >= 0 && smax < 0) || (smax > 0 && smin <= 0); + } else { + return ! ((smin >= 0 && smax < 0) || (smax > 0 && smin <= 0)); + } + } + } + +private: + db::DVector m_emin, m_emax; + bool m_inverse; + bool m_exact; +}; + +/** + * @brief A edge collection iterator + * + * The iterator delivers the edges of the edge collection + */ + +class DB_PUBLIC EdgesIterator +{ +public: + typedef db::Edge value_type; + typedef const db::Edge &reference; + typedef const db::Edge *pointer; + typedef std::forward_iterator_tag iterator_category; + typedef void difference_type; + + /** + * @Returns true, if the iterator is at the end + */ + bool at_end () const + { + return m_from == m_to && m_rec_iter.at_end (); + } + + /** + * @brief Increment + */ + EdgesIterator &operator++ () + { + inc (); + set (); + return *this; + } + + /** + * @brief Access + */ + reference operator* () const + { + if (m_rec_iter.at_end ()) { + return *m_from; + } else { + return m_edge; + } + } + + /** + * @brief Access + */ + pointer operator-> () const + { + if (m_rec_iter.at_end ()) { + return &*m_from; + } else { + return &m_edge; + } + } + +private: + friend class Edges; + + typedef db::layer edge_layer_type; + typedef edge_layer_type::iterator iterator_type; + + db::RecursiveShapeIterator m_rec_iter; + db::ICplxTrans m_iter_trans; + db::Edge m_edge; + iterator_type m_from, m_to; + + /** + * @brief ctor from a recursive shape iterator + */ + EdgesIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) + : m_rec_iter (iter), m_iter_trans (trans), m_from (), m_to () + { + // NOTE: the following initialization appears to be required on some compilers + // (specifically MacOS/clang) to ensure the proper initialization of the iterators + m_from = m_to; + set (); + } + + /** + * @brief ctor from a range of edges inside a vector + */ + EdgesIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // no required yet: set (); + } + + /** + * @brief Establish the iterator at the current position + */ + void set () + { + while (! m_rec_iter.at_end () && ! m_rec_iter.shape ().is_edge ()) { + inc (); + } + if (! m_rec_iter.at_end ()) { + m_rec_iter.shape ().edge (m_edge); + m_edge.transform (m_iter_trans * m_rec_iter.trans ()); + } + } + + /** + * @brief Increment the iterator + */ + void inc () + { + if (! m_rec_iter.at_end ()) { + ++m_rec_iter; + } else { + ++m_from; + } + } +}; + +/** + * @brief An edge set + * + * An edge set is basically a collection of edges. They do not necessarily need to form closed contours. + * Edges can be manipulated in various ways. Edge sets closely cooperate with the Region class which is a + * set of polygons. + * + * Edge sets have some methods in common with regions. Edge sets can also be merged, which means that + * edges which are continuations of other edges are joined. + * + * Edge sets can contain degenerated edges. Such edges are some which have identical start and end points. + * Such edges are basically points which have some applications, i.e. as markers for certain locations. + */ + +class DB_PUBLIC Edges +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::Edge edge_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type distance_type; + typedef db::Edge::distance_type length_type; + typedef EdgesIterator const_iterator; + enum BoolOp { Or, Not, Xor, And }; + + /** + * @brief Default constructor + * + * This constructor creates an empty edge set. + */ + Edges () + : m_edges (false), m_merged_edges (false) + { + init (); + } + + /** + * @brief Constructor from an object + * + * Creates a region representing a single instance of that object. + * The object is converted to a polygon and the edges of that polygon are inserted. + */ + template + Edges (const Sh &s) + : m_edges (false), m_merged_edges (false) + { + init (); + insert (s); + } + + /** + * @brief Sequence constructor + * + * Creates a region from a sequence of objects. The objects can be edges, boxes, + * polygons, paths or shapes. This version accepts iterators of the begin ... end + * style. + */ + template + Edges (const Iter &b, const Iter &e) + : m_edges (false), m_merged_edges (false) + { + init (); + reserve (e - b); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + /** + * @brief Constructor from a RecursiveShapeIterator + * + * Creates a region from a recursive shape iterator. This allows to feed an edge set + * from a hierarchy of cells. + * + * If as_edges is false, only edges will be taken from the recursive shape iterator. + * That is somewhat more efficient since it can avoid a copy in some cases. If as_edges + * is false, shapes will be converted to edges. + */ + Edges (const RecursiveShapeIterator &si, bool as_edges = true); + + /** + * @brief Constructor from a RecursiveShapeIterator with a transformation + * + * Creates a region from a recursive shape iterator. This allows to feed an edge set + * from a hierarchy of cells. The transformation is useful to scale to a specific + * DBU for example. + * + * If as_edges is true, only edges will be taken from the recursive shape iterator. + * That is somewhat more efficient since it can avoid a copy in some cases. If as_edges + * is false, shapes will be converted to edges. + */ + Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges = true, bool merged_semantics = true); + + /** + * @brief Enable progress reporting + * + * @param progress_text The description text of the progress object + */ + void enable_progress (const std::string &progress_desc = std::string ()); + + /** + * @brief Disable progress reporting + */ + void disable_progress (); + + /** + * @brief Iterator of the edge set + * + * The iterator delivers the edges of the edge set. + * It follows the at_end semantics. + */ + const_iterator begin () const + { + if (has_valid_edges ()) { + return const_iterator (m_edges.get_layer ().begin (), m_edges.get_layer ().end ()); + } else { + return const_iterator (m_iter, m_iter_trans); + } + } + + /** + * @brief Returns the merged edges if merge semantics applies + * + * If merge semantics is not enabled, this iterator delivers the individual edges. + */ + const_iterator begin_merged () const; + + /** + * @brief Delivers a RecursiveShapeIterator pointing to the edges plus the necessary transformation + */ + std::pair begin_iter () const; + + /** + * @brief Delivers a RecursiveShapeIterator pointing to the merged edges plus the necessary transformation + */ + std::pair begin_merged_iter () const; + + /** + * @brief Insert an edge into the edge set + */ + void insert (const db::Edge &edge); + + /** + * @brief Insert a box into the edge set + * + * This method will insert all edges the box is composed of. + */ + void insert (const db::Box &box); + + /** + * @brief Insert a path into the edge set + * + * This method will insert all edges the path is composed of. + */ + void insert (const db::Path &path); + + /** + * @brief Insert a simple polygon into the edge set + * + * This method will insert all edges the polygon is composed of. + */ + void insert (const db::SimplePolygon &polygon); + + /** + * @brief Insert a polygon into the edge set + * + * This method will insert all edges the polygon is composed of. + */ + void insert (const db::Polygon &polygon); + + /** + * @brief Insert a shape into the region + * + * If the shape is a polygon-type, the shape is converted to a + * polygon and it's edges are inserted into the edge set. + * If the shape is an edge, the edge is inserted into the edge set. + */ + void insert (const db::Shape &shape) + { + if (shape.is_edge ()) { + insert (shape.edge ()); + } else if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon polygon; + shape.polygon (polygon); + for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + insert (*e); + } + } + } + + /** + * @brief Insert a transformed shape into the edge set + */ + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_edge ()) { + insert (edge_type (trans * shape.edge ())); + } else if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon polygon; + shape.polygon (polygon); + for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + insert (edge_type (trans * *e)); + } + } + } + + /** + * @brief Returns true if the region is empty + */ + bool empty () const + { + return has_valid_edges () && m_edges.empty (); + } + + /** + * @brief Returns the number of polygons in the region + */ + size_t size () const; + + /** + * @brief Returns a string representing the region + * + * nmax specifies how many polygons are included (set to std::numeric_limits::max() for "all". + */ + std::string to_string (size_t nmax = 10) const; + + /** + * @brief Clear the edge set + */ + void clear (); + + /** + * @brief Reserve memory for the given number of edges + */ + void reserve (size_t n) + { + m_edges.reserve (db::Edge::tag (), n); + } + + /** + * @brief Sets the merged-semantics flag + * + * If merged semantics is enabled (the default), coherent polygons will be considered + * as single regions and artificial edges such as cut-lines will not be considered. + * Merged semantics thus is equivalent to considering coherent areas rather than + * single polygons. + */ + void set_merged_semantics (bool f); + + /** + * @brief Gets the merged-semantics flag + */ + bool merged_semantics () const + { + return m_merged_semantics; + } + + /** + * @brief Returns true if the region is merged + */ + bool is_merged () const + { + return m_is_merged; + } + + /** + * @brief Returns the total length of the edges + * Merged semantics applies. In merged semantics, the length is the correct total length of the edges. + * Without merged semantics, overlapping parts are counted twice. + * + * If a box is given, the computation is restricted to that box. + * Edges coincident with the box edges are counted only if the form outer edges at the box edge. + */ + length_type length (const db::Box &box = db::Box ()) const; + + /** + * @brief Returns the bounding box of the region + */ + Box bbox () const + { + ensure_bbox_valid (); + return m_bbox; + } + + /** + * @brief Filters the edge set + * + * This method will keep all edges for which the filter returns true. + * Merged semantics applies. + */ + Edges &filter (EdgeFilterBase &filter) + { + edge_iterator_type ew = m_edges.get_layer ().begin (); + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + if (filter.selected (*e)) { + if (ew == m_edges.get_layer ().end ()) { + m_edges.get_layer ().insert (*e); + ew = m_edges.get_layer ().end (); + } else { + m_edges.get_layer ().replace (ew++, *e); + } + } + } + m_edges.get_layer ().erase (ew, m_edges.get_layer ().end ()); + m_merged_edges.clear (); + m_is_merged = m_merged_semantics; + m_iter = db::RecursiveShapeIterator (); + return *this; + } + + /** + * @brief Returns the filtered edges + * + * This method will return a new region with only those edges which + * conform to the filter criterion. + * Merged semantics applies. + */ + Edges filtered (const EdgeFilterBase &filter) const + { + Edges d; + for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { + if (filter.selected (*e)) { + d.insert (*e); + } + } + return d; + } + + /** + * @brief Returns all edges found in the other edge collection as well + * + * If "invert" is true, all edges not found in the other collection + * are returned. + */ + Edges in (const Edges &other, bool invert) const; + + /** + * @brief Transform the edge set + */ + template + Edges &transform (const T &trans) + { + if (! trans.is_unity ()) { + ensure_valid_edges (); + for (edge_iterator_type e = m_edges.get_layer ().begin (); e != m_edges.get_layer ().end (); ++e) { + m_edges.get_layer ().replace (e, e->transformed (trans)); + } + m_iter_trans = db::ICplxTrans (trans) * m_iter_trans; + m_bbox_valid = false; + } + return *this; + } + + /** + * @brief Returns the transformed edge set + */ + template + Edges transformed (const T &trans) const + { + Edges d (*this); + d.transform (trans); + return d; + } + + /** + * @brief Swap with the other region + */ + void swap (db::Edges &other); + + /** + * @brief returns the extended edges + * + * Edges are extended by creating a rectangle on each edge. The rectangle is constructed on the + * edge by applying the extensions given by ext_o, ext_b, ext_e, ext_i at the outside, the + * beginning, the end and the inside. + * If the edge is laid flat pointing from left to right, the outside is at the top, the inside + * is at the bottom. + * + * For degenerated edges with length 0, the orientation is assumed to the horizontal. The extended + * method creates a rectangle with specified dimensions: ext_b to the left, ext_o to the top, ext_i to the bottom + * and ext_e to the right. + * + * If the joined parameter is set to true, adjacent edges are joined before the extension is applied. + * A the join points, the extension is created similar to what the sizing function does. + * + * Note: the output is given as an out parameter since because of the include hierarchy we can't use + * Region as a return value directly. + * + * Merged semantics applies. + */ + void extended (Region &output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join = false) const; + + /** + * @brief Returns edges (point-like) representing the start part of the edges + * + * The length of the part can be choosen by length or a fraction of the original length. + * If length and fraction are 0, a point at the beginning of the edge will be created. + * If length is non-zero and fraction is 0, a segment in the direction of the edge + * with the given length is created, even if the length is larger than the original + * edge. + * + * If fraction is given and length is 0, the segment will have a length which is the specified + * fraction of the original edge. + * If both values are given, the resulting edge will have a length which is the maximum of + * both the fixed length and the length derived from the fraction. + * + * Length and fraction can be negative in which case the resulting segment will point + * in the opposite direction of the original edge. + * + * Merged semantics applies. + */ + Edges start_segments (length_type length, double fraction) const; + + /** + * @brief Returns edges (point-like) representing the end part of the edges + * + * This method behaves similar to \start_segments but creates segments at the end of + * the edges. + * + * Merged semantics applies. + */ + Edges end_segments (length_type length, double fraction) const; + + /** + * @brief Returns edges (point-like) representing the center of the edges + * + * This method behaves similar to \start_segments but creates segments at the centers of + * the edges. + * + * Merged semantics applies. + */ + Edges centers (length_type length, double fraction) const; + + /** + * @brief Boolean AND operator + * + * This operation returns the parts of the edges which coincide with edges from "other" + * After this operation the edges are not necessarily merged. + */ + Edges operator& (const Edges &other) const + { + return boolean (&other, And); + } + + /** + * @brief In-place boolean AND operator + */ + Edges &operator&= (const Edges &other) + { + inplace_boolean (&other, And); + return *this; + } + + /** + * @brief Boolean AND operator with a region + * + * This operation returns the parts of the edges which are inside the given region. + * Edges on the borders of the polygons are included in the edge set. + * As a side effect, the edges are made non-intersecting by introducing cut points where + * edges intersect. + */ + Edges operator& (const Region &other) const + { + Edges d (*this); + d &= other; + return d; + } + + /** + * @brief In-place boolean AND operator with a region + */ + Edges &operator&= (const Region &other) + { + edge_region_op (other, false /*inside*/, true /*include borders*/); + return *this; + } + + /** + * @brief Boolean NOT operator + * + * This operation returns the parts of the edges which do not coincide with edges from "other" + * After this operation the edges are not necessarily merged. + */ + Edges operator- (const Edges &other) const + { + return boolean (&other, Not); + } + + /** + * @brief In-place boolean NOT operator + */ + Edges &operator-= (const Edges &other) + { + inplace_boolean (&other, Not); + return *this; + } + + /** + * @brief Boolean NOT operator with a region + * + * This operation returns the parts of the edges which are outside the given region. + * Edges on the borders of the polygons are removed from the edge set. + * As a side effect, the edges are made non-intersecting by introducing cut points where + * edges intersect. + */ + Edges operator- (const Region &other) const + { + Edges d (*this); + d -= other; + return d; + } + + /** + * @brief In-place boolean NOT operator with a region + */ + Edges &operator-= (const Region &other) + { + edge_region_op (other, true /*outside*/, true /*include borders*/); + return *this; + } + + /** + * @brief Boolean XOR operator + * + * This operation returns the parts of the edges which do not coincide with edges from "other" + * and vice versa. + * After this operation the edges are not necessarily merged. + */ + Edges operator^ (const Edges &other) const + { + return boolean (&other, Xor); + } + + /** + * @brief In-place boolean XOR operator + */ + Edges &operator^= (const Edges &other) + { + inplace_boolean (&other, Xor); + return *this; + } + + /** + * @brief Joining of edge sets + * + * This method will combine the edges from "other" with the egdes of "this". + * After this operation the edges are not necessarily merged. + */ + Edges operator+ (const Edges &other) const + { + Edges d (*this); + d += other; + return d; + } + + /** + * @brief In-place joining of edge sets + */ + Edges &operator+= (const Edges &other); + + /** + * @brief Boolean OR operator + * + * This method will combine the edges from "other" with the egdes of "this". + * After this operation the edges are usually merged. + */ + Edges operator| (const Edges &other) const + { + return boolean (&other, Or); + } + + /** + * @brief In-place boolean OR operator + */ + Edges &operator|= (const Edges &other) + { + inplace_boolean (&other, Or); + return *this; + } + + /** + * @brief Select the edges inside the given region + * + * This method will select the edges inside the given region. + * Edges on the border of the region won't be selected. + * As a side effect, the edges are made non-intersecting by introducing cut points where + * edges intersect. + */ + Edges &select_inside_part (const Region &other) + { + edge_region_op (other, false /*inside*/, false /*don't include borders*/); + return *this; + } + + /** + * @brief Returns the edges inside the given region + * + * This is an out-of-place version of "select_inside_part". + */ + Edges inside_part (const Region &other) const + { + Edges d (*this); + d.select_inside_part (other); + return d; + } + + /** + * @brief Select the edge parts outside of the given region + * + * This method will select the edge parts outside of the given region. + * Edges on the border of the region won't be selected. + * As a side effect, the edges are made non-intersecting by introducing cut points where + * edges intersect. + */ + Edges &select_outside_part (const Region &other) + { + edge_region_op (other, true /*outside*/, false /*don't include borders*/); + return *this; + } + + /** + * @brief Returns the edge parts outside of the given region + * + * This is an out-of-place version of "select_outside_part". + */ + Edges outside_part (const Region &other) const + { + Edges d (*this); + d.select_outside_part (other); + return d; + } + + /** + * @brief Selects all edges of this edge set which overlap or touch with polygons from the region + * + * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be + * selected as a whole. + */ + Edges &select_interacting (const Region &other); + + /** + * @brief Returns all edges of this edge set which overlap or touch with polygons from the region + * + * This method is an out-of-place version of select_interacting. + */ + Edges selected_interacting (const Region &other) const + { + Edges d (*this); + d.select_interacting (other); + return d; + } + + /** + * @brief Selects all edges of this edge set which do not overlap or touch with polygons from the region + * + * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be + * selected as a whole. + */ + Edges &select_not_interacting (const Region &other); + + /** + * @brief Returns all edges of this edge set which do not overlap or touch with polygons from the region + * + * This method is an out-of-place version of select_not_interacting. + */ + Edges selected_not_interacting (const Region &other) const + { + Edges d (*this); + d.select_not_interacting (other); + return d; + } + + /** + * @brief Selects all edges of this edge set which overlap or touch with edges from the other edge set + * + * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be + * selected as a whole. + */ + Edges &select_interacting (const Edges &other); + + /** + * @brief Returns all edges of this edge set which overlap or touch with edges from the other edge set + * + * This method is an out-of-place version of select_interacting. + */ + Edges selected_interacting (const Edges &other) const + { + Edges d (*this); + d.select_interacting (other); + return d; + } + + /** + * @brief Selects all edges of this edge set which do not overlap or touch with edges from the other edge set + * + * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be + * selected as a whole. + */ + Edges &select_not_interacting (const Edges &other); + + /** + * @brief Returns all edges of this edge set which do not overlap or touch with edges from the other edge set + * + * This method is an out-of-place version of select_not_interacting. + */ + Edges selected_not_interacting (const Edges &other) const + { + Edges d (*this); + d.select_not_interacting (other); + return d; + } + + /** + * @brief Merge the edge set + * + * This method merges the edges of the edge set if they are not merged already. + * It returns a reference to this edge set. + * Edges are merged by joining them if one edge is a continuation of another. + * An out-of-place merge version is "merged". + */ + Edges &merge () + { + if (! is_merged ()) { + inplace_boolean (0, Or); + } + return *this; + } + + /* + * @brief Returns the merged edge set + * + * This is the out-of-place merge. It returns a new edge set but does not modify + * the edge set it is called on. An in-place version is "merge". + */ + Edges merged () const + { + return boolean (0, Or); + } + + /** + * @brief Applies a width check and returns EdgePairs which correspond to violation markers + * + * The width check will create a edge pairs if the width of the area between the + * edges is less than the specified threshold d. Without "whole_edges", the parts of + * the edges are returned which violate the condition. If "whole_edges" is true, the + * result will contain the complete edges participating in the result. + * + * "Width" refers to the space between the "inside" sides of the edges. + * + * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" + * metrics are available. + * + * ignore_angle allows specification of a maximum angle the edges can have to not participate + * in the check. By choosing 90 degree, edges having an angle of 90 degree and larger are not checked, + * but acute corners are for example. + * + * With min_projection and max_projection it is possible to specify how edges must be related + * to each other. If the length of the projection of either edge on the other is >= min_projection + * or < max_projection, the edges are considered for the check. + * + * The order of the edges in the resulting edge pairs is undefined. + * + * Merged semantics applies. + */ + EdgePairs width_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return run_check (db::WidthRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a space check and returns EdgePairs which correspond to violation markers + * + * "Space" refers to the space between the "outside" sides of the edges. + * + * For the parameters see \width_check. The space check reports edges for which the space is + * less than the specified threshold d. + * + * Merged semantics applies. + */ + EdgePairs space_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return run_check (db::SpaceRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies an enclosing check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "inside" side of the edge from this edge set, the orientation is parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs enclosing_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return run_check (db::OverlapRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies an overlap check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "inside" side of the edge from this edge set, the orientation is anti-parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs overlap_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return run_check (db::WidthRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies an separation check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "outside" side of the edge from this edge set, the orientation is anti-parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs separation_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return run_check (db::SpaceRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a inside check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "outide" side of the edge from this edge set, the orientation is parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs inside_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return run_check (db::InsideRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Returns the nth edge + * + * This method will force the edges to be inside the edge vector and will invalidate any iterator. + * If that happens, the method may be costly. + * The iterator should be used whenever possible. + */ + const db::Edge *nth (size_t n) const + { + ensure_valid_edges (); + return n < m_edges.size () ? &m_edges.get_layer ().begin () [n] : 0; + } + + /** + * @brief Returns true, if the edge set has valid edges stored within itself + */ + bool has_valid_edges () const + { + return m_iter.at_end (); + } + + /** + * @brief Ensures the edge collection has valid edges + * + * This method is const since it has const semantics. + */ + void ensure_valid_edges () const; + + /** + * @brief Ensures the edge collection has valid merged edges + * + * It will make sure that begin_merged will deliver an + * iterator to an edge with a unique memory location. + */ + void ensure_valid_merged_edges () const; + + /** + * @brief Equality + */ + bool operator== (const db::Edges &other) const; + + /** + * @brief Inequality + */ + bool operator!= (const db::Edges &other) const + { + return !operator== (other); + } + + /** + * @brief Less operator + */ + bool operator< (const db::Edges &other) const; + +private: + typedef db::layer edge_layer_type; + typedef edge_layer_type::iterator edge_iterator_type; + + bool m_is_merged; + bool m_merged_semantics; + mutable db::Shapes m_edges; + mutable db::Shapes m_merged_edges; + mutable db::Box m_bbox; + mutable bool m_bbox_valid; + mutable bool m_merged_edges_valid; + mutable db::RecursiveShapeIterator m_iter; + db::ICplxTrans m_iter_trans; + bool m_report_progress; + std::string m_progress_desc; + + void init (); + void invalidate_cache (); + void set_valid_edges (); + void ensure_bbox_valid () const; + void ensure_merged_edges_valid () const; + EdgePairs run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + void inplace_boolean (const Edges *other, BoolOp op); + Edges boolean (const Edges *other, BoolOp op) const; + void edge_region_op (const Region &other, bool outside, bool include_borders); +}; + +} // namespace db + +namespace tl +{ + /** + * @brief The type traits for the edges type + */ + template <> + struct type_traits : public type_traits + { + typedef true_tag supports_extractor; + typedef true_tag supports_to_string; + typedef true_tag has_less_operator; + typedef true_tag has_equal_operator; + }; + +} + +#endif + +#endif + diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index a3161329d..b700600ff 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -145,8 +145,10 @@ Region::flat_region () FlatRegion *region = dynamic_cast (mp_delegate); if (! region) { region = new FlatRegion (); - region->RegionDelegate::operator= (*mp_delegate); - region->insert_seq (begin ()); + if (mp_delegate) { + region->RegionDelegate::operator= (*mp_delegate); + region->insert_seq (begin ()); + } set_delegate (region); } From ec638c87b4bd9770ababb99fdc5980ba4c9d7ce6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 9 Nov 2018 00:39:00 +0100 Subject: [PATCH 044/335] Refactoring of Edges - compiles again. --- src/db/db/db.pro | 14 +- src/db/db/dbAsIfFlatEdges.cc | 917 +++++++++++++++++++ src/db/db/dbAsIfFlatEdges.h | 176 ++++ src/db/db/dbAsIfFlatRegion.cc | 7 +- src/db/db/dbEdgeBoolean.cc | 24 + src/db/db/dbEdgeBoolean.h | 241 +++++ src/db/db/dbEdges.cc | 190 ++++ src/db/db/dbEdges.h | 1265 +++++++++++++++++++++++++++ src/db/db/dbEdgesDelegate.cc | 1356 +---------------------------- src/db/db/dbEdgesDelegate.h | 1273 +++------------------------ src/db/db/dbEmptyEdges.cc | 105 +++ src/db/db/dbEmptyEdges.h | 111 +++ src/db/db/dbFlatEdges.cc | 363 ++++++++ src/db/db/dbFlatEdges.h | 203 +++++ src/db/db/dbOriginalLayerEdges.cc | 268 ++++++ src/db/db/dbOriginalLayerEdges.h | 85 ++ src/db/db/dbRegion.h | 2 + 17 files changed, 4100 insertions(+), 2500 deletions(-) create mode 100644 src/db/db/dbAsIfFlatEdges.cc create mode 100644 src/db/db/dbAsIfFlatEdges.h create mode 100644 src/db/db/dbEdgeBoolean.cc create mode 100644 src/db/db/dbEdgeBoolean.h create mode 100644 src/db/db/dbEmptyEdges.cc create mode 100644 src/db/db/dbEmptyEdges.h create mode 100644 src/db/db/dbFlatEdges.cc create mode 100644 src/db/db/dbFlatEdges.h create mode 100644 src/db/db/dbOriginalLayerEdges.cc create mode 100644 src/db/db/dbOriginalLayerEdges.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 8c6cf60a5..b152e3b85 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -125,7 +125,12 @@ SOURCES = \ dbFlatRegion.cc \ dbOriginalLayerRegion.cc \ dbRegionDelegate.cc \ - dbEdgesDelegate.cc + dbEdgesDelegate.cc \ + dbEmptyEdges.cc \ + dbAsIfFlatEdges.cc \ + dbFlatEdges.cc \ + dbEdgeBoolean.cc \ + dbOriginalLayerEdges.cc HEADERS = \ dbArray.h \ @@ -220,7 +225,12 @@ HEADERS = \ dbFlatRegion.h \ dbOriginalLayerRegion.h \ dbRegionDelegate.h \ - dbEdgesDelegate.h + dbEdgesDelegate.h \ + dbEmptyEdges.h \ + dbAsIfFlatEdges.h \ + dbFlatEdges.h \ + dbEdgeBoolean.h \ + dbOriginalLayerEdges.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc new file mode 100644 index 000000000..83ea1d8fb --- /dev/null +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -0,0 +1,917 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbAsIfFlatEdges.h" +#include "dbFlatEdges.h" +#include "dbEmptyEdges.h" +#include "dbEdges.h" +#include "dbEdgeBoolean.h" +#include "dbBoxConvert.h" +#include "dbBoxScanner.h" +#include "dbRegion.h" +#include "dbFlatRegion.h" +#include "dbPolygonTools.h" +#include "dbShapeProcessor.h" + +#include + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// AsIfFlagEdges implementation + +AsIfFlatEdges::AsIfFlatEdges () + : EdgesDelegate (), m_bbox_valid (false) +{ + // .. nothing yet .. +} + +AsIfFlatEdges::~AsIfFlatEdges () +{ + // .. nothing yet .. +} + +std::string +AsIfFlatEdges::to_string (size_t nmax) const +{ + std::ostringstream os; + EdgesIterator p (begin ()); + bool first = true; + for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { + if (! first) { + os << ";"; + } + first = false; + os << p->to_string (); + } + if (! p.at_end ()) { + os << "..."; + } + return os.str (); +} + +namespace +{ + +/** + * @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver + * + * Note: This special scanner uses pointers to two different objects: edges and polygons. + * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate + * pointers to edges. + * + * There is a special box converter which is able to sort that out as well. + */ +template +class edge_to_region_interaction_filter + : public db::box_scanner_receiver +{ +public: + edge_to_region_interaction_filter (OutputContainer &output) + : mp_output (&output) + { + // .. nothing yet .. + } + + void add (const char *o1, size_t p1, const char *o2, size_t p2) + { + const db::Edge *e = 0; + const db::Polygon *p = 0; + + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + if (p1 == 0 && p2 == 1) { + e = reinterpret_cast (o1); + p = reinterpret_cast (o2 - 1); + } else if (p1 == 1 && p2 == 0) { + e = reinterpret_cast (o2); + p = reinterpret_cast (o1 - 1); + } + + if (e && p && m_seen.find (e) == m_seen.end ()) { + if (db::interact (*p, *e)) { + m_seen.insert (e); + mp_output->insert (*e); + } + } + } + +private: + OutputContainer *mp_output; + std::set m_seen; +}; + +/** + * @brief A special box converter that splits the pointers into polygon and edge pointers + */ +struct EdgeOrRegionBoxConverter +{ + typedef db::Box box_type; + + db::Box operator() (const char &c) const + { + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + const char *cp = &c; + if ((size_t (cp) & 1) == 1) { + // it's a polygon + return (reinterpret_cast (cp - 1))->box (); + } else { + // it's an edge + const db::Edge *e = reinterpret_cast (cp); + return db::Box (e->p1 (), e->p2 ()); + } + } +}; + +} + +EdgesDelegate * +AsIfFlatEdges::selected_interacting (const Region &other) const +{ + // shortcuts + if (other.empty () || empty ()) { + return new EmptyEdges (); + } + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + other.size ()); + + AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + + for ( ; ! e.at_end (); ++e) { + scanner.insert ((char *) e.operator-> (), 0); + } + + AddressablePolygonDelivery p = other.addressable_polygons (); + + for ( ; ! p.at_end (); ++p) { + scanner.insert ((char *) p.operator-> () + 1, 1); + } + + std::auto_ptr output (new FlatEdges (true)); + edge_to_region_interaction_filter filter (*output); + EdgeOrRegionBoxConverter bc; + scanner.process (filter, 1, bc); + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdges::selected_not_interacting (const Region &other) const +{ + // shortcuts + if (other.empty () || empty ()) { + return clone (); + } + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + other.size ()); + + AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + + for ( ; ! e.at_end (); ++e) { + scanner.insert ((char *) e.operator-> (), 0); + } + + AddressablePolygonDelivery p = other.addressable_polygons (); + + for ( ; ! p.at_end (); ++p) { + scanner.insert ((char *) p.operator-> () + 1, 1); + } + + std::set interacting; + edge_to_region_interaction_filter > filter (interacting); + EdgeOrRegionBoxConverter bc; + scanner.process (filter, 1, bc); + + std::auto_ptr output (new FlatEdges (true)); + for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { + if (interacting.find (*o) == interacting.end ()) { + output->insert (*o); + } + } + + return output.release (); +} + +namespace +{ + +template +struct JoinEdgesCluster + : public db::cluster, + public db::PolygonSink +{ + typedef db::Edge::coord_type coord_type; + + JoinEdgesCluster (OutputContainer *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) + : mp_output (output), m_ext_b (ext_b), m_ext_e (ext_e), m_ext_o (ext_o), m_ext_i (ext_i) + { + // .. nothing yet .. + } + + void finish () + { + std::multimap objects_by_p1; + std::multimap objects_by_p2; + for (iterator o = begin (); o != end (); ++o) { + if (o->first->p1 () != o->first->p2 ()) { + objects_by_p1.insert (std::make_pair (o->first->p1 (), o)); + objects_by_p2.insert (std::make_pair (o->first->p2 (), o)); + } + } + + while (! objects_by_p2.empty ()) { + + tl_assert (! objects_by_p1.empty ()); + + // Find the beginning of a new sequence + std::multimap::iterator j0 = objects_by_p1.begin (); + std::multimap::iterator j = j0; + do { + std::multimap::iterator jj = objects_by_p2.find (j->first); + if (jj == objects_by_p2.end ()) { + break; + } else { + j = objects_by_p1.find (jj->second->first->p1 ()); + tl_assert (j != objects_by_p1.end ()); + } + } while (j != j0); + + iterator i = j->second; + + // determine a sequence + // TODO: this chooses any solution in case of forks. Choose a specific one? + std::vector pts; + pts.push_back (i->first->p1 ()); + + do { + + // record the next point + pts.push_back (i->first->p2 ()); + + // remove the edge as it's taken + std::multimap::iterator jj; + for (jj = objects_by_p2.find (i->first->p2 ()); jj != objects_by_p2.end () && jj->first == i->first->p2 (); ++jj) { + if (jj->second == i) { + break; + } + } + tl_assert (jj != objects_by_p2.end () && jj->second == i); + objects_by_p2.erase (jj); + objects_by_p1.erase (j); + + // process along the edge to the next one + // TODO: this chooses any solution in case of forks. Choose a specific one? + j = objects_by_p1.find (i->first->p2 ()); + if (j != objects_by_p1.end ()) { + i = j->second; + } else { + break; + } + + } while (true); + + bool cyclic = (pts.back () == pts.front ()); + + if (! cyclic) { + + // non-cyclic sequence + db::Path path (pts.begin (), pts.end (), 0, m_ext_b, m_ext_e, false); + std::vector hull; + path.hull (hull, m_ext_o, m_ext_i); + db::Polygon poly; + poly.assign_hull (hull.begin (), hull.end ()); + put (poly); + + } else { + + // we have a loop: form a contour by using the polygon size functions and a "Not" to form the hole + db::Polygon poly; + poly.assign_hull (pts.begin (), pts.end ()); + + db::EdgeProcessor ep; + db::PolygonGenerator pg (*this, false, true); + + int mode_a = -1, mode_b = -1; + + if (m_ext_o == 0) { + ep.insert (poly, 0); + } else { + db::Polygon sized_poly (poly); + sized_poly.size (m_ext_o, m_ext_o, 2 /*sizing mode*/); + ep.insert (sized_poly, 0); + mode_a = 1; + } + + if (m_ext_i == 0) { + ep.insert (poly, 1); + } else { + db::Polygon sized_poly (poly); + sized_poly.size (-m_ext_i, -m_ext_i, 2 /*sizing mode*/); + ep.insert (sized_poly, 1); + mode_b = 1; + } + + db::BooleanOp2 op (db::BooleanOp::ANotB, mode_a, mode_b); + ep.process (pg, op); + + } + + } + } + + virtual void put (const db::Polygon &polygon) + { + mp_output->insert (polygon); + } + +private: + OutputContainer *mp_output; + coord_type m_ext_b, m_ext_e, m_ext_o, m_ext_i; +}; + +template +struct JoinEdgesClusterCollector + : public db::cluster_collector > +{ + typedef db::Edge::coord_type coord_type; + + JoinEdgesClusterCollector (OutputContainer *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) + : db::cluster_collector > (JoinEdgesCluster (output, ext_b, ext_e, ext_o, ext_i), true) + { + // .. nothing yet .. + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + if (o1->p2 () == o2->p1 () || o1->p1 () == o2->p2 ()) { + db::cluster_collector >::add (o1, p1, o2, p2); + } + } +}; + +} + +RegionDelegate * +AsIfFlatEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const +{ + if (join) { + + std::auto_ptr output (new FlatRegion ()); + JoinEdgesClusterCollector cluster_collector (output.get (), ext_b, ext_e, ext_o, ext_i); + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size ()); + + size_t n = 0; + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + scanner.insert (&*e, n); + ++n; + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + return output.release (); + + } else { + + std::auto_ptr output (new FlatRegion ()); + + for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) { + + db::DVector d; + if (e->is_degenerate ()) { + d = db::DVector (1.0, 0.0); + } else { + d = db::DVector (e->d ()) * (1.0 / e->double_length ()); + } + + db::DVector n (-d.y (), d.x ()); + + db::Point pts[4] = { + db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) + n * double (ext_o)), + db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) + n * double (ext_o)), + db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) - n * double (ext_i)), + db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) - n * double (ext_i)), + }; + + db::Polygon poly; + poly.assign_hull (pts + 0, pts + 4); + output->insert (poly); + + } + + return output.release (); + + } +} + +EdgesDelegate * +AsIfFlatEdges::start_segments (length_type length, double fraction) const +{ + std::auto_ptr edges (new FlatEdges ()); + edges->reserve (size ()); + + // zero-length edges would vanish in merged sematics, so we don't set it now + if (length == 0) { + edges->set_merged_semantics (false); + } + + for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) { + double l = std::max (e->double_length () * fraction, double (length)); + edges->insert (db::Edge (e->p1 (), db::Point (db::DPoint (e->p1 ()) + db::DVector (e->d()) * (l / e->double_length ())))); + } + + return edges.release (); +} + +EdgesDelegate * +AsIfFlatEdges::end_segments (length_type length, double fraction) const +{ + std::auto_ptr edges (new FlatEdges ()); + edges->reserve (size ()); + + // zero-length edges would vanish in merged sematics, so we don't set it now + if (length == 0) { + edges->set_merged_semantics (false); + } + + for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) { + double l = std::max (e->double_length () * fraction, double (length)); + edges->insert (db::Edge (db::Point (db::DPoint (e->p2 ()) - db::DVector (e->d()) * (l / e->double_length ())), e->p2 ())); + } + + return edges.release (); +} + +EdgesDelegate * +AsIfFlatEdges::centers (length_type length, double fraction) const +{ + std::auto_ptr edges (new FlatEdges ()); + edges->reserve (size ()); + + // zero-length edges would vanish in merged sematics, so we don't set it now + if (length == 0) { + edges->set_merged_semantics (false); + } + + for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) { + double l = std::max (e->double_length () * fraction, double (length)); + db::DVector dl = db::DVector (e->d()) * (0.5 * l / e->double_length ()); + db::DPoint center = db::DPoint (e->p1 ()) + db::DVector (e->p2 () - e->p1 ()) * 0.5; + edges->insert (db::Edge (db::Point (center - dl), db::Point (center + dl))); + } + + return edges.release (); +} + +namespace +{ + +/** + * @brief A helper class for the edge interaction functionality which acts as an edge pair receiver + */ +template +class edge_interaction_filter + : public db::box_scanner_receiver +{ +public: + edge_interaction_filter (OutputContainer &output) + : mp_output (&output) + { + // .. nothing yet .. + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + // Select the edges which intersect + if (p1 != p2) { + const db::Edge *o = p1 > p2 ? o2 : o1; + const db::Edge *oo = p1 > p2 ? o1 : o2; + if (o->intersect (*oo)) { + if (m_seen.insert (o).second) { + mp_output->insert (*o); + } + } + } + } + +private: + OutputContainer *mp_output; + std::set m_seen; +}; + +} + +EdgesDelegate * +AsIfFlatEdges::selected_interacting (const Edges &other) const +{ + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + other.size ()); + + AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + + for ( ; ! e.at_end (); ++e) { + scanner.insert (e.operator-> (), 0); + } + + AddressableEdgeDelivery ee = other.addressable_edges (); + + for ( ; ! ee.at_end (); ++ee) { + scanner.insert (ee.operator-> (), 1); + } + + std::auto_ptr output (new FlatEdges (true)); + edge_interaction_filter filter (*output); + scanner.process (filter, 1, db::box_convert ()); + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdges::selected_not_interacting (const Edges &other) const +{ + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + other.size ()); + + AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + + for ( ; ! e.at_end (); ++e) { + scanner.insert (e.operator-> (), 0); + } + + AddressableEdgeDelivery ee = other.addressable_edges (); + + for ( ; ! ee.at_end (); ++ee) { + scanner.insert (ee.operator-> (), 1); + } + + std::set interacting; + edge_interaction_filter > filter (interacting); + scanner.process (filter, 1, db::box_convert ()); + + std::auto_ptr output (new FlatEdges (true)); + for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { + if (interacting.find (*o) == interacting.end ()) { + output->insert (*o); + } + } + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdges::in (const Edges &other, bool invert) const +{ + std::set op; + for (EdgesIterator o (other.begin_merged ()); ! o.at_end (); ++o) { + op.insert (*o); + } + + std::auto_ptr new_region (new FlatEdges (false)); + + for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { + if ((op.find (*o) == op.end ()) == invert) { + new_region->insert (*o); + } + } + + return new_region.release (); +} + +size_t +AsIfFlatEdges::size () const +{ + size_t n = 0; + for (EdgesIterator p (begin ()); ! p.at_end (); ++p) { + ++n; + } + return n; +} + +AsIfFlatEdges::length_type +AsIfFlatEdges::length (const db::Box &box) const +{ + distance_type l = 0; + + for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) { + + if (box.empty () || (box.contains (e->p1 ()) && box.contains (e->p2 ()))) { + l += e->length (); + } else { + + std::pair ce = e->clipped (box); + if (ce.first) { + + db::Coord dx = ce.second.dx (); + db::Coord dy = ce.second.dy (); + db::Coord x = ce.second.p1 ().x (); + db::Coord y = ce.second.p1 ().y (); + if ((dx == 0 && x == box.left () && dy < 0) || + (dx == 0 && x == box.right () && dy > 0) || + (dy == 0 && y == box.top () && dx < 0) || + (dy == 0 && y == box.bottom () && dx > 0)) { + // not counted -> box is at outside side of the edge + } else { + l += ce.second.length (); + } + + } + + } + + } + + return l; +} + +Box AsIfFlatEdges::bbox () const +{ + if (! m_bbox_valid) { + m_bbox = compute_bbox (); + m_bbox_valid = true; + } + return m_bbox; +} + +Box AsIfFlatEdges::compute_bbox () const +{ + db::Box b; + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + b += e->bbox (); + } + return b; +} + +void AsIfFlatEdges::update_bbox (const db::Box &b) +{ + m_bbox = b; + m_bbox_valid = true; +} + +void AsIfFlatEdges::invalidate_bbox () +{ + m_bbox_valid = false; +} + +EdgesDelegate * +AsIfFlatEdges::filtered (const EdgeFilterBase &filter) const +{ + std::auto_ptr new_region (new FlatEdges ()); + + for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_region->insert (*p); + } + } + + return new_region.release (); +} + +namespace +{ + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + * + * If will perform a edge by edge check using the provided EdgeRelationFilter + */ +class Edge2EdgeCheck + : public db::box_scanner_receiver +{ +public: + Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool requires_different_layers) + : mp_check (&check), mp_output (&output) + { + m_requires_different_layers = requires_different_layers; + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + // Overlap or inside checks require input from different layers + if (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0) { + + // ensure that the first check argument is of layer 1 and the second of + // layer 2 (unless both are of the same layer) + int l1 = int (p1 & size_t (1)); + int l2 = int (p2 & size_t (1)); + + db::EdgePair ep; + if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { + mp_output->insert (ep); + } + + } + } + +private: + const EdgeRelationFilter *mp_check; + EdgePairs *mp_output; + bool m_requires_different_layers; +}; + +} + +EdgePairs +AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + EdgePairs result; + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + (other ? other->size () : 0)); + + size_t n = 0; + for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) { + scanner.insert (&*e, n); + n += 2; + } + + if (other) { + n = 1; + for (EdgesIterator e (other->begin_merged ()); ! e.at_end (); ++e) { + scanner.insert (&*e, n); + n += 2; + } + } + + EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (other != 0); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + Edge2EdgeCheck edge_check (check, result, other != 0); + scanner.process (edge_check, d, db::box_convert ()); + + return result; +} + +EdgesDelegate * +AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const +{ + std::auto_ptr output (new FlatEdges (true)); + EdgeBooleanClusterCollector cluster_collector (output.get (), op); + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + (other ? other->size () : 0)); + + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (&*e, 0); + } + } + if (other) { + for (EdgesIterator e (other->begin ()); ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (&*e, 1); + } + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdges::edge_region_op (const Region &other, bool outside, bool include_borders) const +{ + // shortcuts + if (other.empty ()) { + if (! outside) { + return new EmptyEdges (); + } else { + return clone (); + } + } else if (empty ()) { + return new EmptyEdges (); + } + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { + if (p->box ().touches (bbox ())) { + ep.insert (*p, 0); + } + } + + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + ep.insert (*e, 1); + } + + std::auto_ptr output (new FlatEdges (false)); + db::EdgeShapeGenerator cc (output->raw_edges (), true /*clear*/); + db::EdgePolygonOp op (outside, include_borders); + ep.process (cc, op); + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdges::add (const Edges &other) const +{ + FlatEdges *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + std::auto_ptr new_edges (new FlatEdges (*other_flat)); + new_edges->set_is_merged (false); + new_edges->invalidate_cache (); + + size_t n = new_edges->raw_edges ().size () + size (); + + new_edges->reserve (n); + + for (EdgesIterator p (begin ()); ! p.at_end (); ++p) { + new_edges->raw_edges ().insert (*p); + } + + return new_edges.release (); + + } else { + + std::auto_ptr new_edges (new FlatEdges (false /*not merged*/)); + + size_t n = size () + other.size (); + + new_edges->reserve (n); + + for (EdgesIterator p (begin ()); ! p.at_end (); ++p) { + new_edges->raw_edges ().insert (*p); + } + for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { + new_edges->raw_edges ().insert (*p); + } + + return new_edges.release (); + + } +} + +bool +AsIfFlatEdges::equals (const Edges &other) const +{ + if (empty () != other.empty ()) { + return false; + } + if (size () != other.size ()) { + return false; + } + EdgesIterator o1 (begin ()); + EdgesIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return false; + } + ++o1; + ++o2; + } + return true; +} + +bool +AsIfFlatEdges::less (const Edges &other) const +{ + if (empty () != other.empty ()) { + return empty () < other.empty (); + } + if (size () != other.size ()) { + return (size () < other.size ()); + } + EdgesIterator o1 (begin ()); + EdgesIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return *o1 < *o2; + } + ++o1; + ++o2; + } + return false; +} + +} + diff --git a/src/db/db/dbAsIfFlatEdges.h b/src/db/db/dbAsIfFlatEdges.h new file mode 100644 index 000000000..adead24af --- /dev/null +++ b/src/db/db/dbAsIfFlatEdges.h @@ -0,0 +1,176 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbAsIfFlatEdges +#define HDR_dbAsIfFlatEdges + +#include "dbCommon.h" + +#include "dbEdgesDelegate.h" + +namespace db { + +/** + * @brief Provides default flat implementations + */ +class DB_PUBLIC AsIfFlatEdges + : public EdgesDelegate +{ +public: + AsIfFlatEdges (); + virtual ~AsIfFlatEdges (); + + virtual size_t size () const; + virtual std::string to_string (size_t) const; + virtual distance_type length (const db::Box &) const; + virtual Box bbox () const; + + virtual EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairs enclosing_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::OverlapRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairs overlap_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairs separation_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairs inside_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::InsideRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter) + { + return filtered (filter); + } + + virtual EdgesDelegate *filtered (const EdgeFilterBase &) const; + + virtual EdgesDelegate *merged_in_place () + { + return merged (); + } + + virtual EdgesDelegate *merged () const + { + return boolean (0, EdgeOr); + } + + virtual EdgesDelegate *and_with (const Edges &other) const + { + return boolean (&other, EdgeAnd); + } + + virtual EdgesDelegate *and_with (const Region &other) const + { + return edge_region_op (other, false /*inside*/, true /*include borders*/); + } + + virtual EdgesDelegate *not_with (const Edges &other) const + { + return boolean (&other, EdgeNot); + } + + virtual EdgesDelegate *not_with (const Region &other) const + { + return edge_region_op (other, true /*outside*/, true /*include borders*/); + } + + virtual EdgesDelegate *xor_with (const Edges &other) const + { + return boolean (&other, EdgeXor); + } + + virtual EdgesDelegate *or_with (const Edges &other) const + { + return boolean (&other, EdgeOr); + } + + virtual EdgesDelegate *add_in_place (const Edges &other) + { + return add (other); + } + + virtual EdgesDelegate *add (const Edges &other) const; + + virtual EdgesDelegate *inside_part (const Region &other) const + { + return edge_region_op (other, false /*inside*/, false /*don't include borders*/); + } + + virtual EdgesDelegate *outside_part (const Region &other) const + { + return edge_region_op (other, true /*outside*/, false /*don't include borders*/); + } + + virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const; + virtual EdgesDelegate *start_segments (length_type length, double fraction) const; + virtual EdgesDelegate *end_segments (length_type length, double fraction) const; + virtual EdgesDelegate *centers (length_type length, double fraction) const; + + virtual EdgesDelegate *selected_interacting (const Edges &) const; + virtual EdgesDelegate *selected_not_interacting (const Edges &) const; + virtual EdgesDelegate *selected_interacting (const Region &) const; + virtual EdgesDelegate *selected_not_interacting (const Region &) const; + + virtual EdgesDelegate *in (const Edges &, bool) const; + + virtual bool equals (const Edges &other) const; + virtual bool less (const Edges &other) const; + +protected: + void update_bbox (const db::Box &box); + void invalidate_bbox (); + +private: + AsIfFlatEdges &operator= (const AsIfFlatEdges &other); + + mutable bool m_bbox_valid; + mutable db::Box m_bbox; + + virtual db::Box compute_bbox () const; + EdgePairs run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + EdgesDelegate *boolean (const Edges *other, EdgeBoolOp op) const; + EdgesDelegate *edge_region_op (const Region &other, bool outside, bool include_borders) const; +}; + +} + +#endif + diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 3f4b41003..50bb03d83 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -433,9 +433,10 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse) scanner.insert ((char *) p.operator-> () + 1, 1); } - other.ensure_valid_merged_edges (); - for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); + AddressableEdgeDelivery e (other.addressable_edges ()); + + for ( ; ! e.at_end (); ++e) { + scanner.insert ((char *) e.operator-> (), 0); } std::auto_ptr output (new FlatRegion (false)); diff --git a/src/db/db/dbEdgeBoolean.cc b/src/db/db/dbEdgeBoolean.cc new file mode 100644 index 000000000..52a6ec7de --- /dev/null +++ b/src/db/db/dbEdgeBoolean.cc @@ -0,0 +1,24 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbEdgeBoolean.h" + diff --git a/src/db/db/dbEdgeBoolean.h b/src/db/db/dbEdgeBoolean.h new file mode 100644 index 000000000..b9e794eb0 --- /dev/null +++ b/src/db/db/dbEdgeBoolean.h @@ -0,0 +1,241 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbEdgeBoolean +#define HDR_dbEdgeBoolean + +#include "dbEdge.h" +#include "dbEdgesDelegate.h" +#include "dbBoxScanner.h" + +#include "tlIntervalMap.h" + +namespace db +{ + +struct OrJoinOp +{ + void operator() (int &v, int n) + { + v += n; + } +}; + +struct AndJoinOp +{ + void operator() (int &v, int n) + { + if (n == 0) { + v = 0; + } + } +}; + +struct NotJoinOp +{ + void operator() (int &v, int n) + { + if (n != 0) { + v = 0; + } + } +}; + +struct XorJoinOp +{ + void operator() (int &v, int n) + { + if (n != 0) { + if (v == 0) { + v = (n > 0 ? 1 : -1); + } else { + v = 0; + } + } + } +}; + +template +struct EdgeBooleanCluster + : public db::cluster +{ + typedef db::Edge::coord_type coord_type; + + EdgeBooleanCluster (OutputContainer *output, EdgeBoolOp op) + : mp_output (output), m_op (op) + { + // .. nothing yet .. + } + + void finish () + { + // determine base edge (longest overall edge) + + // shortcut for single edge + if (begin () + 1 == end ()) { + if (begin ()->second == 0) { + if (m_op != EdgeAnd) { + mp_output->insert (*(begin ()->first)); + } + } else { + if (m_op != EdgeAnd && m_op != EdgeNot) { + mp_output->insert (*(begin ()->first)); + } + } + return; + } + + db::Edge r = *begin ()->first; + double l1 = 0.0, l2 = r.double_length (); + double n = 1.0 / l2; + db::Point p1 = r.p1 (), p2 = r.p2 (); + + for (iterator o = begin () + 1; o != end (); ++o) { + double ll1 = db::sprod (db::Vector (o->first->p1 () - r.p1 ()), r.d ()) * n; + double ll2 = db::sprod (db::Vector (o->first->p2 () - r.p1 ()), r.d ()) * n; + if (ll1 < l1) { + p1 = o->first->p1 (); + l1 = ll1; + } + if (ll2 < l1) { + p1 = o->first->p2 (); + l1 = ll2; + } + if (ll1 > l2) { + p2 = o->first->p1 (); + l2 = ll1; + } + if (ll2 > l2) { + p2 = o->first->p2 (); + l2 = ll2; + } + } + + db::Vector d = db::Vector (p2 - p1); + n = 1.0 / d.double_length (); + + OrJoinOp or_jop; + AndJoinOp and_jop; + NotJoinOp not_jop; + XorJoinOp xor_jop; + + tl::interval_map a, b; + a.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); + b.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); + + for (iterator o = begin (); o != end (); ++o) { + db::Coord l1 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p1 () - p1), d) * n); + db::Coord l2 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p2 () - p1), d) * n); + if (o->second == 0 || m_op == EdgeOr) { + if (l1 < l2) { + a.add (l1, l2, 1, or_jop); + } else if (l1 > l2) { + a.add (l2, l1, -1, or_jop); + } + } else { + if (l1 < l2) { + b.add (l1, l2, 1, or_jop); + } else { + b.add (l2, l1, -1, or_jop); + } + } + } + + tl::interval_map q; + for (tl::interval_map::const_iterator ia = a.begin (); ia != a.end (); ++ia) { + q.add (ia->first.first, ia->first.second, ia->second > 0 ? 1 : (ia->second < 0 ? -1 : 0), or_jop); + } + + if (b.begin () == b.end ()) { + + // optimize for empty b + if (m_op != EdgeAnd) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + if (ib->second > 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.first * n)), p1 + db::Vector (d * (ib->first.second * n)))); + } else if (ib->second < 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.second * n)), p1 + db::Vector (d * (ib->first.first * n)))); + } + } + } + + } else { + + if (m_op == EdgeAnd) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + q.add (ib->first.first, ib->first.second, ib->second, and_jop); + } + } else if (m_op == EdgeNot) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + q.add (ib->first.first, ib->first.second, ib->second, not_jop); + } + } else if (m_op == EdgeXor) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + q.add (ib->first.first, ib->first.second, ib->second, xor_jop); + } + } + + for (tl::interval_map::const_iterator iq = q.begin (); iq != q.end (); ++iq) { + if (iq->second > 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.first * n)), p1 + db::Vector (d * (iq->first.second * n)))); + } else if (iq->second < 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.second * n)), p1 + db::Vector (d * (iq->first.first * n)))); + } + } + + } + + } + +private: + OutputContainer *mp_output; + db::EdgeBoolOp m_op; +}; + +template +struct EdgeBooleanClusterCollector + : public db::cluster_collector > +{ + EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op) + : db::cluster_collector > (EdgeBooleanCluster (output, op), op != EdgeAnd /*report single*/) + { + // .. nothing yet .. + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + // Select edges which are: + // 1.) not degenerate + // 2.) parallel with some tolerance of roughly 1 dbu + // 3.) connected + if (! o1->is_degenerate () && ! o2->is_degenerate () + && fabs ((double) db::vprod (*o1, *o2)) < db::coord_traits::prec_distance () * std::min (o1->double_length (), o2->double_length ()) + && (o1->p1 () == o2->p1 () || o1->p1 () == o2->p2 () || o1->p2 () == o2->p1 () || o1->p2 () == o2->p2 () || o1->coincident (*o2))) { + db::cluster_collector >::add (o1, p1, o2, p2); + } + } +}; + +} + +#endif + diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 7ef4b2ffd..421612a05 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -20,6 +20,195 @@ */ +#include "dbEdges.h" +#include "dbOriginalLayerEdges.h" +#include "dbEmptyEdges.h" +#include "dbFlatEdges.h" +#include "dbRegion.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// Edges implementation + +Edges::Edges () + : mp_delegate (new EmptyEdges ()) +{ + // .. nothing yet .. +} + +Edges::Edges (EdgesDelegate *delegate) + : mp_delegate (delegate) +{ + // .. nothing yet .. +} + +Edges::Edges (const Edges &other) + : mp_delegate (other.mp_delegate->clone ()) +{ + // .. nothing yet .. +} + +Edges::~Edges () +{ + delete mp_delegate; + mp_delegate = 0; +} + +Edges &Edges::operator= (const Edges &other) +{ + if (this != &other) { + set_delegate (other.mp_delegate->clone ()); + } + return *this; +} + +Edges::Edges (const RecursiveShapeIterator &si, bool as_edges) +{ + if (! as_edges) { + mp_delegate = new OriginalLayerEdges (si); + } else { + FlatEdges *fe = new FlatEdges (); + mp_delegate = fe; + for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { + fe->insert (s.shape (), s.trans ()); + } + } +} + +Edges::Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges, bool merged_semantics) +{ + if (! as_edges) { + mp_delegate = new OriginalLayerEdges (si, trans, merged_semantics); + } else { + FlatEdges *fe = new FlatEdges (); + fe->set_merged_semantics (merged_semantics); + mp_delegate = fe; + for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { + fe->insert (s.shape (), trans * s.trans ()); + } + } +} + +const db::RecursiveShapeIterator & +Edges::iter () const +{ + static db::RecursiveShapeIterator def_iter; + const db::RecursiveShapeIterator *i = mp_delegate->iter (); + return *(i ? i : &def_iter); +} + +void +Edges::set_delegate (EdgesDelegate *delegate) +{ + if (delegate != mp_delegate) { + delete mp_delegate; + mp_delegate = delegate; + } +} + +void +Edges::clear () +{ + set_delegate (new EmptyEdges ()); +} + +void +Edges::reserve (size_t n) +{ + flat_edges ()->reserve (n); +} + +void Edges::extended (Region &output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const +{ + output.set_delegate (mp_delegate->extended (ext_b, ext_e, ext_o, ext_i, join)); +} + +template +Edges &Edges::transform (const T &trans) +{ + flat_edges ()->transform (trans); + return *this; +} + +// explicit instantiations +template Edges &Edges::transform (const db::ICplxTrans &); +template Edges &Edges::transform (const db::Trans &); + +template +void Edges::insert (const Sh &shape) +{ + flat_edges ()->insert (shape); +} + +template void Edges::insert (const db::Box &); +template void Edges::insert (const db::SimplePolygon &); +template void Edges::insert (const db::Polygon &); +template void Edges::insert (const db::Path &); +template void Edges::insert (const db::Edge &); + +void Edges::insert (const db::Shape &shape) +{ + flat_edges ()->insert (shape); +} + +template +void Edges::insert (const db::Shape &shape, const T &trans) +{ + flat_edges ()->insert (shape, trans); +} + +template void Edges::insert (const db::Shape &, const db::ICplxTrans &); +template void Edges::insert (const db::Shape &, const db::Trans &); + +FlatEdges * +Edges::flat_edges () +{ + FlatEdges *edges = dynamic_cast (mp_delegate); + if (! edges) { + edges = new FlatEdges (); + if (mp_delegate) { + edges->EdgesDelegate::operator= (*mp_delegate); + edges->insert_seq (begin ()); + } + set_delegate (edges); + } + + return edges; +} + +} + +namespace tl +{ + template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::Edges &b) + { + db::Edge p; + + if (! ex.try_read (p)) { + return false; + } + b.insert (p); + + while (ex.test (";")) { + ex.read (p); + b.insert (p); + } + + return true; + } + + template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::Edges &b) + { + if (! test_extractor_impl (ex, b)) { + ex.error (tl::to_string (tr ("Expected an edge set specification"))); + } + } +} + +#if 0 +// @@@@@@@@@@@@@@@@@@@@@@@@@@ ORIGINAL @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@qqq #include "dbEdges.h" #include "dbEdgePairs.h" @@ -1371,4 +1560,5 @@ namespace tl } } +#endif diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 3f31351dc..e942a4515 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -20,6 +20,1270 @@ */ +#ifndef HDR_dbEdges +#define HDR_dbEdges + +#include "dbCommon.h" +#include "dbEdgesDelegate.h" +#include "dbRecursiveShapeIterator.h" + +#include "gsiObject.h" + +#include + +namespace db { + +class EdgeFilterBase; +class FlatEdges; +class EmptyEdges; + + +/** + * @brief An edge set iterator + * + * The iterator delivers the edges of the edge set + */ +class DB_PUBLIC EdgesIterator +{ +public: + typedef EdgesIteratorDelegate::value_type value_type; + typedef const value_type &reference; + typedef const value_type *pointer; + typedef std::forward_iterator_tag iterator_category; + typedef void difference_type; + + /** + * @brief Default constructor + */ + EdgesIterator () + : mp_delegate (0) + { + // .. nothing yet .. + } + + /** + * @brief Constructor from a delegate + * The iterator will take ownership over the delegate + */ + EdgesIterator (EdgesIteratorDelegate *delegate) + : mp_delegate (delegate) + { + // .. nothing yet .. + } + + /** + * @brief Destructor + */ + ~EdgesIterator () + { + delete mp_delegate; + mp_delegate = 0; + } + + /** + * @brief Copy constructor and assignment + */ + EdgesIterator (const EdgesIterator &other) + : mp_delegate (0) + { + operator= (other); + } + + /** + * @brief Assignment + */ + EdgesIterator &operator= (const EdgesIterator &other) + { + if (this != &other) { + delete mp_delegate; + mp_delegate = other.mp_delegate ? other.mp_delegate->clone () : 0; + } + return *this; + } + + /** + * @Returns true, if the iterator is at the end + */ + bool at_end () const + { + return mp_delegate == 0 || mp_delegate->at_end (); + } + + /** + * @brief Increment + */ + EdgesIterator &operator++ () + { + if (mp_delegate) { + mp_delegate->increment (); + } + return *this; + } + + /** + * @brief Access + */ + reference operator* () const + { + const value_type *value = operator-> (); + tl_assert (value != 0); + return *value; + } + + /** + * @brief Access + */ + pointer operator-> () const + { + return mp_delegate ? mp_delegate->get () : 0; + } + +private: + EdgesIteratorDelegate *mp_delegate; +}; + +/** + * @brief A helper class allowing delivery of addressable edges + * + * In some applications (i.e. box scanner), edges need to be taken + * by address. The edge set cannot always deliver adressable edges. + * This class help providing this ability by keeping a temporary copy + * if required. + */ + +class DB_PUBLIC AddressableEdgeDelivery +{ +public: + AddressableEdgeDelivery () + : m_iter (), m_valid (false) + { + // .. nothing yet .. + } + + AddressableEdgeDelivery (const EdgesIterator &iter, bool valid) + : m_iter (iter), m_valid (valid) + { + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + } + + bool at_end () const + { + return m_iter.at_end (); + } + + AddressableEdgeDelivery &operator++ () + { + ++m_iter; + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + return *this; + } + + const db::Edge *operator-> () const + { + if (m_valid) { + return m_iter.operator-> (); + } else { + return &m_heap.back (); + } + } + +private: + EdgesIterator m_iter; + bool m_valid; + std::list m_heap; +}; + +class Edges; + +/** + * @brief A base class for polygon filters + */ +class DB_PUBLIC EdgeFilterBase +{ +public: + EdgeFilterBase () { } + virtual ~EdgeFilterBase () { } + + virtual bool selected (const db::Edge &edge) const = 0; +}; + +/** + * @brief An edge length filter for use with Edges::filter or Edges::filtered + * + * This filter has two parameters: lmin and lmax. + * It will filter all edges for which the length is >= lmin and < lmax. + * There is an "invert" flag which allows to select all edges not + * matching the criterion. + */ + +struct DB_PUBLIC EdgeLengthFilter + : public EdgeFilterBase +{ + typedef db::Edge::distance_type length_type; + + /** + * @brief Constructor + * + * @param lmin The minimum length + * @param lmax The maximum length + * @param inverse If set to true, only polygons not matching this criterion will be filtered + */ + EdgeLengthFilter (length_type lmin, length_type lmax, bool inverse) + : m_lmin (lmin), m_lmax (lmax), m_inverse (inverse) + { + // .. nothing yet .. + } + + /** + * @brief Returns true if the edge length matches the criterion + */ + bool selected (const db::Edge &edge) const + { + length_type l = edge.length (); + if (! m_inverse) { + return l >= m_lmin && l < m_lmax; + } else { + return ! (l >= m_lmin && l < m_lmax); + } + } + +private: + length_type m_lmin, m_lmax; + bool m_inverse; +}; + +/** + * @brief An edge orientation filter for use with Edges::filter or Edges::filtered + * + * This filter has two parameters: amin and amax. + * It will filter all edges for which the orientation angle is >= amin and < amax. + * The orientation angle is measured in degree against the x axis in the mathematical sense. + * There is an "invert" flag which allows to select all edges not + * matching the criterion. + */ + +struct DB_PUBLIC EdgeOrientationFilter + : public EdgeFilterBase +{ + /** + * @brief Constructor + * + * @param amin The minimum angle (measured against the x axis) + * @param amax The maximum angle (measured against the x axis) + * @param inverse If set to true, only polygons not matching this criterion will be filtered + * + * This filter will filter out all edges whose angle against x axis + * is larger or equal to amin and less than amax. + */ + EdgeOrientationFilter (double amin, double amax, bool inverse) + : m_inverse (inverse), m_exact (false) + { + m_emin = db::DVector (cos (amin * M_PI / 180.0), sin (amin * M_PI / 180.0)); + m_emax = db::DVector (cos (amax * M_PI / 180.0), sin (amax * M_PI / 180.0)); + } + + /** + * @brief Constructor + * + * @param a The angle (measured against the x axis) + * @param inverse If set to true, only polygons not matching this criterion will be filtered + * + * This filter will filter out all edges whose angle against x axis + * is equal to a. + */ + EdgeOrientationFilter (double a, bool inverse) + : m_inverse (inverse), m_exact (true) + { + m_emin = db::DVector (cos (a * M_PI / 180.0), sin (a * M_PI / 180.0)); + } + + /** + * @brief Returns true if the edge orientation matches the criterion + */ + bool selected (const db::Edge &edge) const + { + int smin = db::vprod_sign (m_emin, db::DVector (edge.d ())); + if (m_exact) { + if (! m_inverse) { + return smin == 0; + } else { + return smin != 0; + } + } else { + int smax = db::vprod_sign (m_emax, db::DVector (edge.d ())); + if (! m_inverse) { + return (smin >= 0 && smax < 0) || (smax > 0 && smin <= 0); + } else { + return ! ((smin >= 0 && smax < 0) || (smax > 0 && smin <= 0)); + } + } + } + +private: + db::DVector m_emin, m_emax; + bool m_inverse; + bool m_exact; +}; + +/** + * @brief An edge set + * + * An edge set is basically a collection of edges. They do not necessarily need to form closed contours. + * Edges can be manipulated in various ways. Edge sets closely cooperate with the Region class which is a + * set of polygons. + * + * Edge sets have some methods in common with regions. Edge sets can also be merged, which means that + * edges which are continuations of other edges are joined. + * + * Edge sets can contain degenerated edges. Such edges are some which have identical start and end points. + * Such edges are basically points which have some applications, i.e. as markers for certain locations. + */ + +class DB_PUBLIC Edges + : public gsi::ObjectBase +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::Edge edge_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type length_type; + typedef coord_traits::distance_type distance_type; + typedef EdgesIterator const_iterator; + + /** + * @brief Default constructor + * + * Creates an empty edge set. + */ + Edges (); + + /** + * @brief Destructor + */ + ~Edges (); + + /** + * @brief Constructor from a delegate + * + * The region will take ownership of the delegate. + */ + Edges (EdgesDelegate *delegate); + + /** + * @brief Copy constructor + */ + Edges (const Edges &other); + + /** + * @brief Assignment + */ + Edges &operator= (const Edges &other); + + /** + * @brief Constructor from an object + * + * Creates an edge set representing a single instance of that object + */ + template + Edges (const Sh &s) + : mp_delegate (0) + { + insert (s); + } + + /** + * @brief Sequence constructor + * + * Creates an edge set from a sequence of objects. The objects can be boxes, + * polygons, paths, edges or shapes. This version accepts iterators of the begin ... end + * style. + */ + template + Edges (const Iter &b, const Iter &e) + : mp_delegate (0) + { + reserve (e - b); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + /** + * @brief Constructor from a RecursiveShapeIterator + * + * Creates an edge set from a recursive shape iterator. This allows to feed an edge set + * from a hierarchy of cells. + */ + Edges (const RecursiveShapeIterator &si, bool as_edges = true); + + /** + * @brief Constructor from a RecursiveShapeIterator with a transformation + * + * Creates an edge set from a recursive shape iterator. This allows to feed an edge set + * from a hierarchy of cells. The transformation is useful to scale to a specific + * DBU for example. + */ + Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges = true, bool merged_semantics = true); + + /** + * @brief Gets the underlying delegate object + */ + EdgesDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Enable progress reporting + * + * @param progress_text The description text of the progress object + */ + void enable_progress (const std::string &desc = std::string ()) + { + mp_delegate->enable_progress (desc); + } + + /** + * @brief Disable progress reporting + */ + void disable_progress () + { + mp_delegate->disable_progress (); + } + + /** + * @brief Iterator of the edge set + * + * The iterator delivers the edges of the edge set. + * It follows the at_end semantics. + */ + const_iterator begin () const + { + return EdgesIterator (mp_delegate->begin ()); + } + + /** + * @brief Returns the merged edges if merge semantics applies + * + * If merge semantics is not enabled, this iterator delivers the individual edges. + */ + const_iterator begin_merged () const + { + return EdgesIterator (mp_delegate->begin_merged ()); + } + + /** + * @brief Delivers a RecursiveShapeIterator pointing to the edges plus the necessary transformation + */ + std::pair begin_iter () const + { + return mp_delegate->begin_iter (); + } + + /** + * @brief Delivers a RecursiveShapeIterator pointing to the merged edges plus the necessary transformation + */ + std::pair begin_merged_iter () const + { + return mp_delegate->begin_merged_iter (); + } + + /** + * @brief Inserts the given shape (working object) into the edge set + */ + template + void insert (const Sh &shape); + + /** + * @brief Insert a shape reference into the edge set + */ + void insert (const db::Shape &shape); + + /** + * @brief Insert a transformed shape into the edge set + */ + template + void insert (const db::Shape &shape, const T &trans); + + /** + * @brief Returns true if the edge set is empty + */ + bool empty () const + { + return mp_delegate->empty (); + } + + /** + * @brief Returns the number of edges in the edge set + */ + size_t size () const + { + return mp_delegate->size (); + } + + /** + * @brief Returns a string representing the edge set + * + * nmax specifies how many edges are included (set to std::numeric_limits::max() for "all". + */ + std::string to_string (size_t nmax = 10) const + { + return mp_delegate->to_string (nmax); + } + + /** + * @brief Clears the edge set + */ + void clear (); + + /** + * @brief Reserve memory for the given number of edges + */ + void reserve (size_t n); + + /** + * @brief Sets the merged-semantics flag + * + * If merged semantics is enabled (the default), colinear edges will be considered + * as single edges. + */ + void set_merged_semantics (bool f) + { + mp_delegate->set_merged_semantics (f); + } + + /** + * @brief Gets the merged-semantics flag + */ + bool merged_semantics () const + { + return mp_delegate->merged_semantics (); + } + + /** + * @brief Enables or disables strict handling + * + * Strict handling means to leave away some optimizations. Specifically the + * output of boolean operations will be merged even if one input is empty. + * Without strict handling, the operation will be optimized and output + * won't be merged. + * + * Strict handling is disabled by default. + */ + void set_strict_handling (bool f) + { + mp_delegate->set_strict_handling (f); + } + + /** + * @brief Gets a valid indicating whether strict handling is enabled + */ + bool strict_handling () const + { + return mp_delegate->strict_handling (); + } + + /** + * @brief Returns true if the edge set is merged + */ + bool is_merged () const + { + return mp_delegate->is_merged (); + } + + /** + * @brief Returns the total length of the edges + * Merged semantics applies. In merged semantics, the length is the correct total length of the edges. + * Without merged semantics, overlapping parts are counted twice. + * + * If a box is given, the computation is restricted to that box. + * Edges coincident with the box edges are counted only if the form outer edges at the box edge. + */ + length_type length (const db::Box &box = db::Box ()) const + { + return mp_delegate->length (box); + } + + /** + * @brief Returns the bounding box of the edge set + */ + Box bbox () const + { + return mp_delegate->bbox (); + } + + /** + * @brief Filters the edges + * + * This method will keep all edges for which the filter returns true. + * Merged semantics applies. In merged semantics, the filter will run over + * all merged edges. + */ + Edges &filter (const EdgeFilterBase &filter) + { + set_delegate (mp_delegate->filter_in_place (filter)); + return *this; + } + + /** + * @brief Returns the filtered edges + * + * This method will return a new region with only those edges which + * conform to the filter criterion. + */ + Edges filtered (const EdgeFilterBase &filter) const + { + return Edges (mp_delegate->filtered (filter)); + } + + /** + * @brief Applies a width check and returns EdgePairs which correspond to violation markers + * + * The width check will create a edge pairs if the width of the area between the + * edges is less than the specified threshold d. Without "whole_edges", the parts of + * the edges are returned which violate the condition. If "whole_edges" is true, the + * result will contain the complete edges participating in the result. + * + * "Width" refers to the space between the "inside" sides of the edges. + * + * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" + * metrics are available. + * + * ignore_angle allows specification of a maximum angle the edges can have to not participate + * in the check. By choosing 90 degree, edges having an angle of 90 degree and larger are not checked, + * but acute corners are for example. + * + * With min_projection and max_projection it is possible to specify how edges must be related + * to each other. If the length of the projection of either edge on the other is >= min_projection + * or < max_projection, the edges are considered for the check. + * + * The order of the edges in the resulting edge pairs is undefined. + * + * Merged semantics applies. + */ + EdgePairs width_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->width_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a space check and returns EdgePairs which correspond to violation markers + * + * "Space" refers to the space between the "outside" sides of the edges. + * + * For the parameters see \width_check. The space check reports edges for which the space is + * less than the specified threshold d. + * + * Merged semantics applies. + */ + EdgePairs space_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->space_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies an enclosing check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "inside" side of the edge from this edge set, the orientation is parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs enclosing_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->enclosing_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies an overlap check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "inside" side of the edge from this edge set, the orientation is anti-parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs overlap_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->overlap_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies an separation check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "outside" side of the edge from this edge set, the orientation is anti-parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs separation_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->separation_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a inside check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "outide" side of the edge from this edge set, the orientation is parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs inside_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->inside_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Transforms the edge set + */ + template + Edges &transform (const T &trans); + + /** + * @brief Returns the transformed edge set + */ + template + Edges transformed (const T &trans) const + { + Edges d (*this); + d.transform (trans); + return d; + } + + /** + * @brief Swaps with the other edge set + */ + void swap (db::Edges &other) + { + std::swap (other.mp_delegate, mp_delegate); + } + + /** + * @brief Merges the edge set + * + * This method merges the edges of the edge set if they are not merged already. + * It returns a reference to this edge set. + * An out-of-place merge version is "merged". + */ + Edges &merge () + { + set_delegate (mp_delegate->merged_in_place ()); + return *this; + } + + /** + * @brief Returns the merged edge set + * + * This is the out-of-place merge. It returns a new edge set but does not modify + * the edge set it is called on. An in-place version is "merge". + */ + Edges merged () const + { + return Edges (mp_delegate->merged ()); + } + + /** + * @brief Boolean AND operator + */ + Edges operator& (const Edges &other) const + { + return Edges (mp_delegate->and_with (other)); + } + + /** + * @brief In-place boolean AND operator + * + * This method does not necessarily merge the edge set. To ensure the edge set + * is merged, call merge afterwards. + */ + Edges &operator&= (const Edges &other) + { + set_delegate (mp_delegate->and_with (other)); + return *this; + } + + /** + * @brief Boolean AND operator with a region + */ + Edges operator& (const Region &other) const + { + return Edges (mp_delegate->and_with (other)); + } + + /** + * @brief In-place boolean AND operator with a region + * + * This method will keep all edges inside the given region. + */ + Edges &operator&= (const Region &other) + { + set_delegate (mp_delegate->and_with (other)); + return *this; + } + + /** + * @brief Boolean NOT operator + */ + Edges operator- (const Edges &other) const + { + return Edges (mp_delegate->not_with (other)); + } + + /** + * @brief In-place boolean NOT operator + * + * This method does not necessarily merge the edge set. To ensure the edge set + * is merged, call merge afterwards. + */ + Edges &operator-= (const Edges &other) + { + set_delegate (mp_delegate->not_with (other)); + return *this; + } + + /** + * @brief Boolean NOT operator with a region + */ + Edges operator- (const Region &other) const + { + return Edges (mp_delegate->and_with (other)); + } + + /** + * @brief In-place boolean NOT operator with a region + * + * This method will remove all edges inside the given region. + */ + Edges &operator-= (const Region &other) + { + set_delegate (mp_delegate->and_with (other)); + return *this; + } + + /** + * @brief Boolean XOR operator + */ + Edges operator^ (const Edges &other) const + { + return Edges (mp_delegate->xor_with (other)); + } + + /** + * @brief In-place boolean XOR operator + * + * This method does not necessarily merge the edge set. To ensure the edge set + * is merged, call merge afterwards. + */ + Edges &operator^= (const Edges &other) + { + set_delegate (mp_delegate->xor_with (other)); + return *this; + } + + /** + * @brief Boolean OR operator + * + * This method merges the edges of both edge sets. + */ + Edges operator| (const Edges &other) const + { + return Edges (mp_delegate->or_with (other)); + } + + /** + * @brief In-place boolean OR operator + */ + Edges &operator|= (const Edges &other) + { + set_delegate (mp_delegate->or_with (other)); + return *this; + } + + /** + * @brief Joining of edge set + * + * This method joins the edge sets but does not merge them afterwards. + */ + Edges operator+ (const Edges &other) const + { + return Edges (mp_delegate->add (other)); + } + + /** + * @brief In-place edge set joining + */ + Edges &operator+= (const Edges &other) + { + set_delegate (mp_delegate->add_in_place (other)); + return *this; + } + + /** + * @brief returns the extended edges + * + * Edges are extended by creating a rectangle on each edge. The rectangle is constructed on the + * edge by applying the extensions given by ext_o, ext_b, ext_e, ext_i at the outside, the + * beginning, the end and the inside. + * If the edge is laid flat pointing from left to right, the outside is at the top, the inside + * is at the bottom. + * + * For degenerated edges with length 0, the orientation is assumed to the horizontal. The extended + * method creates a rectangle with specified dimensions: ext_b to the left, ext_o to the top, ext_i to the bottom + * and ext_e to the right. + * + * If the joined parameter is set to true, adjacent edges are joined before the extension is applied. + * A the join points, the extension is created similar to what the sizing function does. + * + * Note: the output is given as an out parameter since because of the include hierarchy we can't use + * Region as a return value directly. + * + * Merged semantics applies. + */ + void extended (Region &output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join = false) const; + + /** + * @brief Returns edges (point-like) representing the start part of the edges + * + * The length of the part can be choosen by length or a fraction of the original length. + * If length and fraction are 0, a point at the beginning of the edge will be created. + * If length is non-zero and fraction is 0, a segment in the direction of the edge + * with the given length is created, even if the length is larger than the original + * edge. + * + * If fraction is given and length is 0, the segment will have a length which is the specified + * fraction of the original edge. + * If both values are given, the resulting edge will have a length which is the maximum of + * both the fixed length and the length derived from the fraction. + * + * Length and fraction can be negative in which case the resulting segment will point + * in the opposite direction of the original edge. + * + * Merged semantics applies. + */ + Edges start_segments (length_type length, double fraction) const + { + return Edges (mp_delegate->start_segments (length, fraction)); + } + + /** + * @brief Returns edges (point-like) representing the end part of the edges + * + * This method behaves similar to \start_segments but creates segments at the end of + * the edges. + * + * Merged semantics applies. + */ + Edges end_segments (length_type length, double fraction) const + { + return Edges (mp_delegate->end_segments (length, fraction)); + } + + /** + * @brief Returns edges (point-like) representing the center of the edges + * + * This method behaves similar to \start_segments but creates segments at the centers of + * the edges. + * + * Merged semantics applies. + */ + Edges centers (length_type length, double fraction) const + { + return Edges (mp_delegate->centers (length, fraction)); + } + + /** + * @brief Select the edges inside the given region + * + * This method will select the edges inside the given region. + * Edges on the border of the region won't be selected. + * As a side effect, the edges are made non-intersecting by introducing cut points where + * edges intersect. + */ + Edges &select_inside_part (const Region &other) + { + set_delegate (mp_delegate->inside_part (other)); + return *this; + } + + /** + * @brief Returns the edges inside the given region + * + * This is an out-of-place version of "select_inside_part". + */ + Edges inside_part (const Region &other) const + { + return Edges (mp_delegate->inside_part (other)); + } + + /** + * @brief Select the edge parts outside of the given region + * + * This method will select the edge parts outside of the given region. + * Edges on the border of the region won't be selected. + * As a side effect, the edges are made non-intersecting by introducing cut points where + * edges intersect. + */ + Edges &select_outside_part (const Region &other) + { + set_delegate (mp_delegate->outside_part (other)); + return *this; + } + + /** + * @brief Returns the edge parts outside of the given region + * + * This is an out-of-place version of "select_outside_part". + */ + Edges outside_part (const Region &other) const + { + return Edges (mp_delegate->outside_part (other)); + } + + /** + * @brief Selects all edges of this edge set which overlap or touch with polygons from the region + * + * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be + * selected as a whole. + */ + Edges &select_interacting (const Region &other) + { + set_delegate (mp_delegate->selected_interacting (other)); + return *this; + } + + /** + * @brief Returns all edges of this edge set which overlap or touch with polygons from the region + * + * This method is an out-of-place version of select_interacting. + */ + Edges selected_interacting (const Region &other) const + { + return Edges (mp_delegate->selected_interacting (other)); + } + + /** + * @brief Selects all edges of this edge set which do not overlap or touch with polygons from the region + * + * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be + * selected as a whole. + */ + Edges &select_not_interacting (const Region &other) + { + set_delegate (mp_delegate->selected_not_interacting (other)); + return *this; + } + + /** + * @brief Returns all edges of this edge set which do not overlap or touch with polygons from the region + * + * This method is an out-of-place version of select_not_interacting. + */ + Edges selected_not_interacting (const Region &other) const + { + return Edges (mp_delegate->selected_not_interacting (other)); + } + + /** + * @brief Selects all edges of this edge set which overlap or touch with edges from the other edge set + * + * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be + * selected as a whole. + */ + Edges &select_interacting (const Edges &other) + { + set_delegate (mp_delegate->selected_interacting (other)); + return *this; + } + + /** + * @brief Returns all edges of this edge set which overlap or touch with edges from the other edge set + * + * This method is an out-of-place version of select_interacting. + */ + Edges selected_interacting (const Edges &other) const + { + return Edges (mp_delegate->selected_interacting (other)); + } + + /** + * @brief Selects all edges of this edge set which do not overlap or touch with edges from the other edge set + * + * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be + * selected as a whole. + */ + Edges &select_not_interacting (const Edges &other) + { + set_delegate (mp_delegate->selected_not_interacting (other)); + return *this; + } + + /** + * @brief Returns all edges of this edge set which do not overlap or touch with edges from the other edge set + * + * This method is an out-of-place version of select_not_interacting. + */ + Edges selected_not_interacting (const Edges &other) const + { + return Edges (mp_delegate->selected_not_interacting (other)); + } + + /** + * @brief Returns all edges which are in the other edge set + * + * This method will return all edges which are part of another edge set. + * The match is done exactly. + * The "invert" flag can be used to invert the sense, i.e. with + * "invert" set to true, this method will return all edges not + * in the other edge set. + * + * Merged semantics applies. + */ + Edges in (const Edges &other, bool invert = false) const + { + return Edges (mp_delegate->in (other, invert)); + } + + /** + * @brief Returns the nth edge + * + * This operation is only cheap if "has_valid_edges" is true. Otherwise, the + * complexity is O(n). + */ + const db::Edge *nth (size_t n) const + { + return mp_delegate->nth (n); + } + + /** + * @brief Returns true, if the edge set has valid edges stored within itself + * + * If the region has valid edges, it is permissable to use the edge's addresses + * from the iterator. Furthermore, the random access operator nth() is available. + */ + bool has_valid_edges () const + { + return mp_delegate->has_valid_edges (); + } + + /** + * @brief Returns an addressable delivery for edges + * + * This object allows accessing the edges by address, even if they + * are not delivered from a container. The magic is a heap object + * inside the delivery object. Hence, the deliver object must persist + * as long as the addresses are required. + */ + AddressableEdgeDelivery addressable_edges () const + { + return AddressableEdgeDelivery (begin (), has_valid_edges ()); + } + + /** + * @brief Returns true, if the edge set has valid merged edges stored within itself + * + * If the region has valid merged edges, it is permissable to use the edge's addresses + * from the merged edge iterator. Furthermore, the random access operator nth() is available. + */ + bool has_valid_merged_edges () const + { + return mp_delegate->has_valid_merged_edges (); + } + + /** + * @brief Returns an addressable delivery for merged polygons + */ + AddressableEdgeDelivery addressable_merged_edges () const + { + return AddressableEdgeDelivery (begin_merged (), has_valid_merged_edges ()); + } + + /** + * @brief Gets the internal iterator + * + * This method is intended for users who know what they are doing + */ + const db::RecursiveShapeIterator &iter () const; + + /** + * @brief Equality + */ + bool operator== (const db::Edges &other) const + { + return mp_delegate->equals (other); + } + + /** + * @brief Inequality + */ + bool operator!= (const db::Edges &other) const + { + return ! mp_delegate->equals (other); + } + + /** + * @brief Less operator + */ + bool operator< (const db::Edges &other) const + { + return mp_delegate->less (other); + } + +private: + EdgesDelegate *mp_delegate; + + void set_delegate (EdgesDelegate *delegate); + FlatEdges *flat_edges (); +}; + +} // namespace db + +namespace tl +{ + /** + * @brief The type traits for the region type + */ + template <> + struct type_traits : public type_traits + { + typedef true_tag supports_extractor; + typedef true_tag supports_to_string; + typedef true_tag has_less_operator; + typedef true_tag has_equal_operator; + }; + +} + +#endif + +#if 0 +// @@@@@@@@@@@@@@@@@@@@@@@@@@ ORIGINAL @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@qqq #ifndef HDR_dbEdges #define HDR_dbEdges @@ -1248,3 +2512,4 @@ namespace tl #endif +#endif diff --git a/src/db/db/dbEdgesDelegate.cc b/src/db/db/dbEdgesDelegate.cc index b64ee6a76..b545e2fb2 100644 --- a/src/db/db/dbEdgesDelegate.cc +++ b/src/db/db/dbEdgesDelegate.cc @@ -20,1357 +20,65 @@ */ -#if 0 -#include "dbEdges.h" -#include "dbEdgePairs.h" -#include "dbRegion.h" -#include "dbLayoutUtils.h" -#include "dbBoxConvert.h" -#include "dbBoxScanner.h" -#include "dbPolygonTools.h" -#include "dbShapeProcessor.h" - -#include "tlIntervalMap.h" -#include "tlVariant.h" - -#include +#include "dbEdgesDelegate.h" namespace db { -Edges::Edges (const RecursiveShapeIterator &si, bool as_edges) - : m_edges (false), m_merged_edges (false) -{ - init (); - if (! as_edges) { - m_iter = si; - } else { - for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { - insert (s.shape (), s.trans ()); - } - } - m_bbox_valid = false; - m_is_merged = false; -} +// ------------------------------------------------------------------------------------------------------------- -Edges::Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges, bool merged_semantics) - : m_edges (false), m_merged_edges (false) -{ - init (); - if (! as_edges) { - m_iter = si; - m_iter_trans = trans; - } else { - for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { - insert (s.shape (), trans * s.trans ()); - } - } - m_bbox_valid = false; - m_is_merged = false; - m_merged_semantics = merged_semantics; -} - -bool -Edges::operator== (const db::Edges &other) const -{ - if (empty () != other.empty ()) { - return false; - } - if (size () != other.size ()) { - return false; - } - db::Edges::const_iterator o1 = begin (); - db::Edges::const_iterator o2 = other.begin (); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return false; - } - ++o1; - ++o2; - } - return true; -} - -bool -Edges::operator< (const db::Edges &other) const -{ - if (empty () != other.empty ()) { - return empty () < other.empty (); - } - if (size () != other.size ()) { - return (size () < other.size ()); - } - db::Edges::const_iterator o1 = begin (); - db::Edges::const_iterator o2 = other.begin (); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return *o1 < *o2; - } - ++o1; - ++o2; - } - return false; -} - -std::string -Edges::to_string (size_t nmax) const -{ - std::ostringstream os; - const_iterator e = begin (); - bool first = true; - for ( ; ! e.at_end () && nmax != 0; ++e, --nmax) { - if (! first) { - os << ";"; - } - first = false; - os << e->to_string (); - } - if (! e.at_end ()) { - os << "..."; - } - return os.str (); -} - -void -Edges::swap (Edges &other) -{ - std::swap (m_is_merged, other.m_is_merged); - std::swap (m_merged_semantics, other.m_merged_semantics); - m_edges.swap (other.m_edges); - m_merged_edges.swap (other.m_merged_edges); - std::swap (m_bbox, other.m_bbox); - std::swap (m_bbox_valid, other.m_bbox_valid); - std::swap (m_merged_edges_valid, other.m_merged_edges_valid); - std::swap (m_iter, other.m_iter); - std::swap (m_iter_trans, other.m_iter_trans); -} - -void -Edges::invalidate_cache () -{ - m_bbox_valid = false; - m_merged_edges.clear (); - m_merged_edges_valid = false; -} - -void -Edges::disable_progress () +EdgesDelegate::EdgesDelegate () { m_report_progress = false; + m_merged_semantics = true; + m_strict_handling = false; } -void -Edges::enable_progress (const std::string &progress_desc) +EdgesDelegate::EdgesDelegate (const EdgesDelegate &other) +{ + operator= (other); +} + +EdgesDelegate & +EdgesDelegate::operator= (const EdgesDelegate &other) +{ + if (this != &other) { + m_report_progress = other.m_report_progress; + m_merged_semantics = other.m_merged_semantics; + m_strict_handling = other.m_strict_handling; + } + return *this; +} + +EdgesDelegate::~EdgesDelegate () +{ + // .. nothing yet .. +} + +void EdgesDelegate::enable_progress (const std::string &progress_desc) { m_report_progress = true; m_progress_desc = progress_desc; } -Edges::distance_type -Edges::length (const db::Box &box) const -{ - distance_type l = 0; - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - - if (box.empty () || (box.contains (e->p1 ()) && box.contains (e->p2 ()))) { - l += e->length (); - } else { - - std::pair ce = e->clipped (box); - if (ce.first) { - - db::Coord dx = ce.second.dx (); - db::Coord dy = ce.second.dy (); - db::Coord x = ce.second.p1 ().x (); - db::Coord y = ce.second.p1 ().y (); - if ((dx == 0 && x == box.left () && dy < 0) || - (dx == 0 && x == box.right () && dy > 0) || - (dy == 0 && y == box.top () && dx < 0) || - (dy == 0 && y == box.bottom () && dx > 0)) { - // not counted -> box is at outside side of the edge - } else { - l += ce.second.length (); - } - - } - - } - - } - - return l; -} - -Edges -Edges::start_segments (db::Edges::length_type length, double fraction) const -{ - Edges edges; - edges.reserve (m_edges.size ()); - - // zero-length edges would vanish in merged sematics, so we don't set it now - if (length == 0) { - edges.set_merged_semantics (false); - } - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - double l = std::max (e->double_length () * fraction, double (length)); - edges.insert (db::Edge (e->p1 (), db::Point (db::DPoint (e->p1 ()) + db::DVector (e->d()) * (l / e->double_length ())))); - } - - return edges; -} - -Edges -Edges::end_segments (db::Edges::length_type length, double fraction) const -{ - Edges edges; - edges.reserve (m_edges.size ()); - - // zero-length edges would vanish in merged sematics, so we don't set it now - if (length == 0) { - edges.set_merged_semantics (false); - } - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - double l = std::max (e->double_length () * fraction, double (length)); - edges.insert (db::Edge (db::Point (db::DPoint (e->p2 ()) - db::DVector (e->d()) * (l / e->double_length ())), e->p2 ())); - } - - return edges; -} - -Edges -Edges::centers (db::Edges::length_type length, double fraction) const -{ - Edges edges; - edges.reserve (m_edges.size ()); - - // zero-length edges would vanish in merged sematics, so we don't set it now - if (length == 0) { - edges.set_merged_semantics (false); - } - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - double l = std::max (e->double_length () * fraction, double (length)); - db::DVector dl = db::DVector (e->d()) * (0.5 * l / e->double_length ()); - db::DPoint center = db::DPoint (e->p1 ()) + db::DVector (e->p2 () - e->p1 ()) * 0.5; - edges.insert (db::Edge (db::Point (center - dl), db::Point (center + dl))); - } - - return edges; -} - -void -Edges::edge_region_op (const Region &other, bool outside, bool include_borders) -{ - // shortcuts - if (other.empty ()) { - if (! outside) { - clear (); - } - return; - } else if (empty ()) { - return; - } - - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { - if (p->box ().touches (bbox ())) { - ep.insert (*p, 0); - } - } - - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ep.insert (*e, 1); - } - - invalidate_cache (); - - db::EdgeShapeGenerator cc (m_edges, true /*clear*/); - db::EdgePolygonOp op (outside, include_borders); - ep.process (cc, op); - - set_valid_edges (); - - m_is_merged = false; -} - -namespace -{ - -struct OrJoinOp -{ - void operator() (int &v, int n) - { - v += n; - } -}; - -struct AndJoinOp -{ - void operator() (int &v, int n) - { - if (n == 0) { - v = 0; - } - } -}; - -struct NotJoinOp -{ - void operator() (int &v, int n) - { - if (n != 0) { - v = 0; - } - } -}; - -struct XorJoinOp -{ - void operator() (int &v, int n) - { - if (n != 0) { - if (v == 0) { - v = (n > 0 ? 1 : -1); - } else { - v = 0; - } - } - } -}; - -struct EdgeBooleanCluster - : public db::cluster -{ - typedef db::Edge::coord_type coord_type; - - EdgeBooleanCluster (db::Edges *output, db::Edges::BoolOp op) - : mp_output (output), m_op (op) - { - // .. nothing yet .. - } - - void finish () - { - // determine base edge (longest overall edge) - - // shortcut for single edge - if (begin () + 1 == end ()) { - if (begin ()->second == 0) { - if (m_op != db::Edges::And) { - mp_output->insert (*(begin ()->first)); - } - } else { - if (m_op != db::Edges::And && m_op != db::Edges::Not) { - mp_output->insert (*(begin ()->first)); - } - } - return; - } - - db::Edge r = *begin ()->first; - double l1 = 0.0, l2 = r.double_length (); - double n = 1.0 / l2; - db::Point p1 = r.p1 (), p2 = r.p2 (); - - for (iterator o = begin () + 1; o != end (); ++o) { - double ll1 = db::sprod (db::Vector (o->first->p1 () - r.p1 ()), r.d ()) * n; - double ll2 = db::sprod (db::Vector (o->first->p2 () - r.p1 ()), r.d ()) * n; - if (ll1 < l1) { - p1 = o->first->p1 (); - l1 = ll1; - } - if (ll2 < l1) { - p1 = o->first->p2 (); - l1 = ll2; - } - if (ll1 > l2) { - p2 = o->first->p1 (); - l2 = ll1; - } - if (ll2 > l2) { - p2 = o->first->p2 (); - l2 = ll2; - } - } - - db::Vector d = db::Vector (p2 - p1); - n = 1.0 / d.double_length (); - - OrJoinOp or_jop; - AndJoinOp and_jop; - NotJoinOp not_jop; - XorJoinOp xor_jop; - - tl::interval_map a, b; - a.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); - b.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); - - for (iterator o = begin (); o != end (); ++o) { - db::Coord l1 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p1 () - p1), d) * n); - db::Coord l2 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p2 () - p1), d) * n); - if (o->second == 0 || m_op == db::Edges::Or) { - if (l1 < l2) { - a.add (l1, l2, 1, or_jop); - } else if (l1 > l2) { - a.add (l2, l1, -1, or_jop); - } - } else { - if (l1 < l2) { - b.add (l1, l2, 1, or_jop); - } else { - b.add (l2, l1, -1, or_jop); - } - } - } - - tl::interval_map q; - for (tl::interval_map::const_iterator ia = a.begin (); ia != a.end (); ++ia) { - q.add (ia->first.first, ia->first.second, ia->second > 0 ? 1 : (ia->second < 0 ? -1 : 0), or_jop); - } - - if (b.begin () == b.end ()) { - - // optimize for empty b - if (m_op != db::Edges::And) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - if (ib->second > 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.first * n)), p1 + db::Vector (d * (ib->first.second * n)))); - } else if (ib->second < 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.second * n)), p1 + db::Vector (d * (ib->first.first * n)))); - } - } - } - - } else { - - if (m_op == db::Edges::And) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - q.add (ib->first.first, ib->first.second, ib->second, and_jop); - } - } else if (m_op == db::Edges::Not) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - q.add (ib->first.first, ib->first.second, ib->second, not_jop); - } - } else if (m_op == db::Edges::Xor) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - q.add (ib->first.first, ib->first.second, ib->second, xor_jop); - } - } - - for (tl::interval_map::const_iterator iq = q.begin (); iq != q.end (); ++iq) { - if (iq->second > 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.first * n)), p1 + db::Vector (d * (iq->first.second * n)))); - } else if (iq->second < 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.second * n)), p1 + db::Vector (d * (iq->first.first * n)))); - } - } - - } - - } - -private: - db::Edges *mp_output; - db::Edges::BoolOp m_op; -}; - -struct EdgeBooleanClusterCollector - : public db::cluster_collector -{ - EdgeBooleanClusterCollector (db::Edges *output, Edges::BoolOp op) - : db::cluster_collector (EdgeBooleanCluster (output, op), op != Edges::And /*report single*/) - { - // .. nothing yet .. - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - // Select edges which are: - // 1.) not degenerate - // 2.) parallel with some tolerance of roughly 1 dbu - // 3.) connected - if (! o1->is_degenerate () && ! o2->is_degenerate () - && fabs ((double) db::vprod (*o1, *o2)) < db::coord_traits::prec_distance () * std::min (o1->double_length (), o2->double_length ()) - && (o1->p1 () == o2->p1 () || o1->p1 () == o2->p2 () || o1->p2 () == o2->p1 () || o1->p2 () == o2->p2 () || o1->coincident (*o2))) { - db::cluster_collector::add (o1, p1, o2, p2); - } - } -}; - -} - -void -Edges::inplace_boolean (const Edges *other, Edges::BoolOp op) -{ - Edges out = boolean (other, op); - swap (out); -} - -Edges -Edges::boolean (const Edges *other, Edges::BoolOp op) const -{ - Edges output; - EdgeBooleanClusterCollector cluster_collector (&output, op); - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (m_edges.size () + (other ? other->size () : 0)); - - ensure_valid_edges (); - for (const_iterator e = begin (); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); - } - } - if (other) { - other->ensure_valid_edges (); - for (const_iterator e = other->begin (); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 1); - } - } - } - - scanner.process (cluster_collector, 1, db::box_convert ()); - - output.m_is_merged = true; - return output; -} - -void -Edges::ensure_valid_merged_edges () const -{ - // If no merged semantics applies or we will deliver the original - // edges as merged ones, we need to make sure those are valid - // ones (with a unique memory address) - if (! m_merged_semantics || m_is_merged) { - ensure_valid_edges (); - } else { - ensure_merged_edges_valid (); - } -} - -void -Edges::ensure_merged_edges_valid () const -{ - if (! m_merged_edges_valid) { - - m_merged_edges.clear (); - - Edges tmp; - EdgeBooleanClusterCollector cluster_collector (&tmp, Or); - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (m_edges.size ()); - - ensure_valid_edges (); - for (const_iterator e = begin (); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); - } - } - - scanner.process (cluster_collector, 1, db::box_convert ()); - - m_merged_edges.swap (tmp.m_edges); - m_merged_edges_valid = true; - - } -} - -Edges & -Edges::operator+= (const Edges &other) -{ - invalidate_cache (); - - if (! has_valid_edges ()) { - - m_edges.clear (); - - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ++n; - } - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - ++n; - } - - m_edges.reserve (db::Edge::tag (), n); - - for (const_iterator e = begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - - set_valid_edges (); - - } else if (! other.has_valid_edges ()) { - - size_t n = m_edges.size (); - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - ++n; - } - - m_edges.reserve (db::Edge::tag (), n); - - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - - } else { - m_edges.insert (other.m_edges.get_layer ().begin (), other.m_edges.get_layer ().end ()); - } - - m_is_merged = false; - return *this; -} - -namespace -{ - -/** - * @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver - * - * Note: This special scanner uses pointers to two different objects: edges and polygons. - * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate - * pointers to edges. - * - * There is a special box converter which is able to sort that out as well. - */ -template -class edge_to_region_interaction_filter - : public db::box_scanner_receiver -{ -public: - edge_to_region_interaction_filter (OutputContainer &output) - : mp_output (&output) - { - // .. nothing yet .. - } - - void add (const char *o1, size_t p1, const char *o2, size_t p2) - { - const db::Edge *e = 0; - const db::Polygon *p = 0; - - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - if (p1 == 0 && p2 == 1) { - e = reinterpret_cast (o1); - p = reinterpret_cast (o2 - 1); - } else if (p1 == 1 && p2 == 0) { - e = reinterpret_cast (o2); - p = reinterpret_cast (o1 - 1); - } - - if (e && p && m_seen.find (e) == m_seen.end ()) { - if (db::interact (*p, *e)) { - m_seen.insert (e); - mp_output->insert (*e); - } - } - } - -private: - OutputContainer *mp_output; - std::set m_seen; -}; - -/** - * @brief A special box converter that splits the pointers into polygon and edge pointers - */ -struct EdgeOrRegionBoxConverter -{ - typedef db::Box box_type; - - db::Box operator() (const char &c) const - { - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - const char *cp = &c; - if ((size_t (cp) & 1) == 1) { - // it's a polygon - return (reinterpret_cast (cp - 1))->box (); - } else { - // it's an edge - const db::Edge *e = reinterpret_cast (cp); - return db::Box (e->p1 (), e->p2 ()); - } - } -}; - -} - -Edges & -Edges::select_interacting (const Region &other) -{ - // shortcuts - if (other.empty ()) { - clear (); - return *this; - } else if (empty ()) { - return *this; - } - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - AddressablePolygonDelivery p = other.addressable_polygons (); - - for ( ; ! p.at_end (); ++p) { - // NOTE: dirty hack - we can take the address of the polygon because we used ensure_flat_polygons. - scanner.insert ((char *) p.operator-> () + 1, 1); - } - - Edges output; - edge_to_region_interaction_filter filter (output); - EdgeOrRegionBoxConverter bc; - scanner.process (filter, 1, bc); - - swap (output); - return *this; -} - -Edges & -Edges::select_not_interacting (const Region &other) -{ - // shortcuts - if (other.empty () || empty ()) { - return *this; - } - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - AddressablePolygonDelivery p = other.addressable_polygons (); - for ( ; ! p.at_end (); ++p) { - // NOTE: dirty hack - we can take the address of the polygon because we used ensure_flat_polygons. - scanner.insert ((char *) p.operator-> () + 1, 1); - } - - std::set interacting; - edge_to_region_interaction_filter > filter (interacting); - EdgeOrRegionBoxConverter bc; - scanner.process (filter, 1, bc); - - Edges output; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if (interacting.find (*o) == interacting.end ()) { - output.insert (*o); - } - } - - swap (output); - return *this; -} - -namespace -{ - -/** - * @brief A helper class for the edge interaction functionality which acts as an edge pair receiver - */ -template -class edge_interaction_filter - : public db::box_scanner_receiver -{ -public: - edge_interaction_filter (OutputContainer &output) - : mp_output (&output) - { - // .. nothing yet .. - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - // Select the edges which intersect - if (p1 != p2) { - const db::Edge *o = p1 > p2 ? o2 : o1; - const db::Edge *oo = p1 > p2 ? o1 : o2; - if (o->intersect (*oo)) { - if (m_seen.insert (o).second) { - mp_output->insert (*o); - } - } - } - } - -private: - OutputContainer *mp_output; - std::set m_seen; -}; - -} - -Edges & -Edges::select_interacting (const Edges &other) -{ - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, 0); - } - other.ensure_valid_edges (); - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert (&*e, 1); - } - - Edges output; - edge_interaction_filter filter (output); - scanner.process (filter, 1, db::box_convert ()); - - swap (output); - return *this; -} - -Edges & -Edges::select_not_interacting (const Edges &other) -{ - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, 0); - } - other.ensure_valid_edges (); - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert (&*e, 1); - } - - std::set interacting; - edge_interaction_filter > filter (interacting); - scanner.process (filter, 1, db::box_convert ()); - - Edges output; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if (interacting.find (*o) == interacting.end ()) { - output.insert (*o); - } - } - - swap (output); - return *this; -} - -namespace -{ - -struct JoinEdgesCluster - : public db::cluster -{ - typedef db::Edge::coord_type coord_type; - - JoinEdgesCluster (db::Region *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) - : mp_output (output), m_ext_b (ext_b), m_ext_e (ext_e), m_ext_o (ext_o), m_ext_i (ext_i) - { - // .. nothing yet .. - } - - void finish () - { - std::multimap objects_by_p1; - std::multimap objects_by_p2; - for (iterator o = begin (); o != end (); ++o) { - if (o->first->p1 () != o->first->p2 ()) { - objects_by_p1.insert (std::make_pair (o->first->p1 (), o)); - objects_by_p2.insert (std::make_pair (o->first->p2 (), o)); - } - } - - while (! objects_by_p2.empty ()) { - - tl_assert (! objects_by_p1.empty ()); - - // Find the beginning of a new sequence - std::multimap::iterator j0 = objects_by_p1.begin (); - std::multimap::iterator j = j0; - do { - std::multimap::iterator jj = objects_by_p2.find (j->first); - if (jj == objects_by_p2.end ()) { - break; - } else { - j = objects_by_p1.find (jj->second->first->p1 ()); - tl_assert (j != objects_by_p1.end ()); - } - } while (j != j0); - - iterator i = j->second; - - // determine a sequence - // TODO: this chooses any solution in case of forks. Choose a specific one? - std::vector pts; - pts.push_back (i->first->p1 ()); - - do { - - // record the next point - pts.push_back (i->first->p2 ()); - - // remove the edge as it's taken - std::multimap::iterator jj; - for (jj = objects_by_p2.find (i->first->p2 ()); jj != objects_by_p2.end () && jj->first == i->first->p2 (); ++jj) { - if (jj->second == i) { - break; - } - } - tl_assert (jj != objects_by_p2.end () && jj->second == i); - objects_by_p2.erase (jj); - objects_by_p1.erase (j); - - // process along the edge to the next one - // TODO: this chooses any solution in case of forks. Choose a specific one? - j = objects_by_p1.find (i->first->p2 ()); - if (j != objects_by_p1.end ()) { - i = j->second; - } else { - break; - } - - } while (true); - - bool cyclic = (pts.back () == pts.front ()); - - if (! cyclic) { - - // non-cyclic sequence - db::Path path (pts.begin (), pts.end (), 0, m_ext_b, m_ext_e, false); - std::vector hull; - path.hull (hull, m_ext_o, m_ext_i); - db::Polygon poly; - poly.assign_hull (hull.begin (), hull.end ()); - mp_output->insert (poly); - - } else { - - // we have a loop: form a contour by using the polygon size functions and a "Not" to form the hole - db::Polygon poly; - poly.assign_hull (pts.begin (), pts.end ()); - - db::EdgeProcessor ep; - db::RegionPolygonSink ps (*mp_output, false); - db::PolygonGenerator pg (ps, false, true); - - int mode_a = -1, mode_b = -1; - - if (m_ext_o == 0) { - ep.insert (poly, 0); - } else { - db::Polygon sized_poly (poly); - sized_poly.size (m_ext_o, m_ext_o, 2 /*sizing mode*/); - ep.insert (sized_poly, 0); - mode_a = 1; - } - - if (m_ext_i == 0) { - ep.insert (poly, 1); - } else { - db::Polygon sized_poly (poly); - sized_poly.size (-m_ext_i, -m_ext_i, 2 /*sizing mode*/); - ep.insert (sized_poly, 1); - mode_b = 1; - } - - db::BooleanOp2 op (db::BooleanOp::ANotB, mode_a, mode_b); - ep.process (pg, op); - - } - - } - } - -private: - db::Region *mp_output; - coord_type m_ext_b, m_ext_e, m_ext_o, m_ext_i; -}; - -struct JoinEdgesClusterCollector - : public db::cluster_collector -{ - typedef db::Edge::coord_type coord_type; - - JoinEdgesClusterCollector (db::Region *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) - : db::cluster_collector (JoinEdgesCluster (output, ext_b, ext_e, ext_o, ext_i), true) - { - // .. nothing yet .. - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - if (o1->p2 () == o2->p1 () || o1->p1 () == o2->p2 ()) { - db::cluster_collector::add (o1, p1, o2, p2); - } - } -}; - -} - -void -Edges::extended (Region &output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const -{ - if (join) { - - JoinEdgesClusterCollector cluster_collector (&output, ext_b, ext_e, ext_o, ext_i); - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size ()); - - ensure_valid_edges (); - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - scanner.insert (&*e, n); - ++n; - } - - scanner.process (cluster_collector, 1, db::box_convert ()); - - } else { - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - - db::DVector d; - if (e->is_degenerate ()) { - d = db::DVector (1.0, 0.0); - } else { - d = db::DVector (e->d ()) * (1.0 / e->double_length ()); - } - - db::DVector n (-d.y (), d.x ()); - - db::Point pts[4] = { - db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) + n * double (ext_o)), - db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) + n * double (ext_o)), - db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) - n * double (ext_i)), - db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) - n * double (ext_i)), - }; - - db::Polygon poly; - poly.assign_hull (pts + 0, pts + 4); - output.insert (poly); - - } - - } -} - -Edges -Edges::in (const Edges &other, bool invert) const -{ - std::set op; - for (const_iterator o = other.begin_merged (); ! o.at_end (); ++o) { - op.insert (*o); - } - - Edges r; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if ((op.find (*o) == op.end ()) == invert) { - r.insert (*o); - } - } - - return r; -} - -void -Edges::init () +void EdgesDelegate::disable_progress () { m_report_progress = false; - m_bbox_valid = true; - m_is_merged = true; - m_merged_semantics = true; - m_merged_edges_valid = false; } -void -Edges::ensure_bbox_valid () const -{ - if (! m_bbox_valid) { - m_bbox = db::Box (); - for (const_iterator e = begin (); ! e.at_end (); ++e) { - m_bbox += db::Box (e->p1 (), e->p2 ()); - } - m_bbox_valid = true; - } -} - -Edges::const_iterator -Edges::begin_merged () const -{ - if (! m_merged_semantics || m_is_merged) { - return begin (); - } else { - ensure_merged_edges_valid (); - return db::EdgesIterator (m_merged_edges.get_layer ().begin (), m_merged_edges.get_layer ().end ()); - } -} - -std::pair -Edges::begin_iter () const -{ - if (has_valid_edges ()) { - return std::make_pair (db::RecursiveShapeIterator (m_edges), db::ICplxTrans ()); - } else { - return std::make_pair (m_iter, m_iter_trans); - } -} - -std::pair -Edges::begin_merged_iter () const -{ - if (! m_merged_semantics || m_is_merged) { - return begin_iter (); - } else { - ensure_merged_edges_valid (); - return std::make_pair (db::RecursiveShapeIterator (m_merged_edges), db::ICplxTrans ()); - } -} - -void -Edges::insert (const db::Edge &edge) -{ - ensure_valid_edges (); - m_edges.insert (edge); - m_is_merged = false; - invalidate_cache (); -} - -void -Edges::insert (const db::Box &box) -{ - if (! box.empty ()) { - ensure_valid_edges (); - m_edges.insert (db::Edge (box.lower_left (), box.upper_left ())); - m_edges.insert (db::Edge (box.upper_left (), box.upper_right ())); - m_edges.insert (db::Edge (box.upper_right (), box.lower_right ())); - m_edges.insert (db::Edge (box.lower_right (), box.lower_left ())); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Edges::insert (const db::Path &path) -{ - if (path.points () > 0) { - insert (path.polygon ()); - } -} - -void -Edges::insert (const db::Polygon &polygon) -{ - ensure_valid_edges (); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - m_is_merged = false; - invalidate_cache (); -} - -void -Edges::insert (const db::SimplePolygon &polygon) -{ - ensure_valid_edges (); - for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - m_is_merged = false; - invalidate_cache (); -} - -void -Edges::clear () -{ - m_edges.clear (); - m_bbox = db::Box (); - m_bbox_valid = true; - m_is_merged = true; - m_merged_edges.clear (); - m_merged_edges_valid = true; - m_iter = db::RecursiveShapeIterator (); - m_iter_trans = db::ICplxTrans (); -} - -namespace -{ - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - * - * If will perform a edge by edge check using the provided EdgeRelationFilter - */ -class Edge2EdgeCheck - : public db::box_scanner_receiver -{ -public: - Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool requires_different_layers) - : mp_check (&check), mp_output (&output) - { - m_requires_different_layers = requires_different_layers; - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - // Overlap or inside checks require input from different layers - if (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0) { - - // ensure that the first check argument is of layer 1 and the second of - // layer 2 (unless both are of the same layer) - int l1 = int (p1 & size_t (1)); - int l2 = int (p2 & size_t (1)); - - db::EdgePair ep; - if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { - mp_output->insert (ep); - } - - } - } - -private: - const EdgeRelationFilter *mp_check; - EdgePairs *mp_output; - bool m_requires_different_layers; -}; - -} - -EdgePairs -Edges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const -{ - EdgePairs result; - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + (other ? other->size () : 0)); - - ensure_valid_merged_edges (); - size_t n = 0; - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, n); - n += 2; - } - - if (other) { - other->ensure_valid_merged_edges (); - n = 1; - for (const_iterator e = other->begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, n); - n += 2; - } - } - - EdgeRelationFilter check (rel, d, metrics); - check.set_include_zero (other != 0); - check.set_whole_edges (whole_edges); - check.set_ignore_angle (ignore_angle); - check.set_min_projection (min_projection); - check.set_max_projection (max_projection); - - Edge2EdgeCheck edge_check (check, result, other != 0); - scanner.process (edge_check, d, db::box_convert ()); - - return result; -} - -size_t -Edges::size () const -{ - if (! has_valid_edges ()) { - // If we have an iterator, we have to do it the hard way .. - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ++n; - } - return n; - } else { - return m_edges.size (); - } -} - -void -Edges::set_merged_semantics (bool f) +void EdgesDelegate::set_merged_semantics (bool f) { if (f != m_merged_semantics) { m_merged_semantics = f; - m_merged_edges.clear (); - m_merged_edges_valid = false; + merged_semantics_changed (); } } -void -Edges::set_valid_edges () +void EdgesDelegate::set_strict_handling (bool f) { - m_iter = db::RecursiveShapeIterator (); + m_strict_handling = f; } -void -Edges::ensure_valid_edges () const -{ - if (! has_valid_edges ()) { - - m_edges.clear (); - - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ++n; - } - m_edges.reserve (db::Edge::tag (), n); - - for (const_iterator e = begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - - m_iter = db::RecursiveShapeIterator (); - - } } -} // namespace db - -namespace tl -{ - template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::Edges &b) - { - db::Edge e; - - if (! ex.try_read (e)) { - return false; - } - b.insert (e); - - while (ex.test (";")) { - ex.read (e); - b.insert (e); - } - - return true; - } - - template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::Edges &b) - { - if (! test_extractor_impl (ex, b)) { - ex.error (tl::to_string (tr ("Expected an edge collection specification"))); - } - } -} - -#endif - diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 7fefbe4a8..5f97137fe 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -20,289 +20,50 @@ */ -#if 0 -#ifndef HDR_dbEdges -#define HDR_dbEdges +#ifndef HDR_dbEdgesDelegate +#define HDR_dbEdgesDelegate #include "dbCommon.h" -#include "dbTypes.h" #include "dbEdge.h" -#include "dbTrans.h" -#include "dbShape.h" -#include "dbShapes.h" -#include "dbShapes2.h" -#include "dbEdgePairRelations.h" #include "dbEdgePairs.h" -#include "dbRecursiveShapeIterator.h" -#include "tlString.h" +#include "dbEdgePairRelations.h" + +#include namespace db { -class Edges; +/** + * @brief A common definition for the boolean operations available on edges + */ +enum EdgeBoolOp { EdgeOr, EdgeNot, EdgeXor, EdgeAnd }; + +class RecursiveShapeIterator; +class EdgeFilterBase; +class RegionDelegate; /** - * @brief A base class for polygon filters + * @brief The edge set iterator delegate */ -class DB_PUBLIC EdgeFilterBase +class DB_PUBLIC EdgesIteratorDelegate { public: - EdgeFilterBase () { } - virtual ~EdgeFilterBase () { } + EdgesIteratorDelegate () { } + virtual ~EdgesIteratorDelegate () { } - virtual bool selected (const db::Edge &edge) const = 0; + typedef db::Edge value_type; + + virtual bool at_end () const = 0; + virtual void increment () = 0; + virtual const value_type *get () const = 0; + virtual EdgesIteratorDelegate *clone () const = 0; }; /** - * @brief An edge length filter for use with Edges::filter or Edges::filtered - * - * This filter has two parameters: lmin and lmax. - * It will filter all edges for which the length is >= lmin and < lmax. - * There is an "invert" flag which allows to select all edges not - * matching the criterion. + * @brief The delegate for the actual edge set implementation */ - -struct DB_PUBLIC EdgeLengthFilter - : public EdgeFilterBase -{ - typedef db::Edge::distance_type length_type; - - /** - * @brief Constructor - * - * @param lmin The minimum length - * @param lmax The maximum length - * @param inverse If set to true, only polygons not matching this criterion will be filtered - */ - EdgeLengthFilter (length_type lmin, length_type lmax, bool inverse) - : m_lmin (lmin), m_lmax (lmax), m_inverse (inverse) - { - // .. nothing yet .. - } - - /** - * @brief Returns true if the edge length matches the criterion - */ - bool selected (const db::Edge &edge) const - { - length_type l = edge.length (); - if (! m_inverse) { - return l >= m_lmin && l < m_lmax; - } else { - return ! (l >= m_lmin && l < m_lmax); - } - } - -private: - length_type m_lmin, m_lmax; - bool m_inverse; -}; - -/** - * @brief An edge orientation filter for use with Edges::filter or Edges::filtered - * - * This filter has two parameters: amin and amax. - * It will filter all edges for which the orientation angle is >= amin and < amax. - * The orientation angle is measured in degree against the x axis in the mathematical sense. - * There is an "invert" flag which allows to select all edges not - * matching the criterion. - */ - -struct DB_PUBLIC EdgeOrientationFilter - : public EdgeFilterBase -{ - /** - * @brief Constructor - * - * @param amin The minimum angle (measured against the x axis) - * @param amax The maximum angle (measured against the x axis) - * @param inverse If set to true, only polygons not matching this criterion will be filtered - * - * This filter will filter out all edges whose angle against x axis - * is larger or equal to amin and less than amax. - */ - EdgeOrientationFilter (double amin, double amax, bool inverse) - : m_inverse (inverse), m_exact (false) - { - m_emin = db::DVector (cos (amin * M_PI / 180.0), sin (amin * M_PI / 180.0)); - m_emax = db::DVector (cos (amax * M_PI / 180.0), sin (amax * M_PI / 180.0)); - } - - /** - * @brief Constructor - * - * @param a The angle (measured against the x axis) - * @param inverse If set to true, only polygons not matching this criterion will be filtered - * - * This filter will filter out all edges whose angle against x axis - * is equal to a. - */ - EdgeOrientationFilter (double a, bool inverse) - : m_inverse (inverse), m_exact (true) - { - m_emin = db::DVector (cos (a * M_PI / 180.0), sin (a * M_PI / 180.0)); - } - - /** - * @brief Returns true if the edge orientation matches the criterion - */ - bool selected (const db::Edge &edge) const - { - int smin = db::vprod_sign (m_emin, db::DVector (edge.d ())); - if (m_exact) { - if (! m_inverse) { - return smin == 0; - } else { - return smin != 0; - } - } else { - int smax = db::vprod_sign (m_emax, db::DVector (edge.d ())); - if (! m_inverse) { - return (smin >= 0 && smax < 0) || (smax > 0 && smin <= 0); - } else { - return ! ((smin >= 0 && smax < 0) || (smax > 0 && smin <= 0)); - } - } - } - -private: - db::DVector m_emin, m_emax; - bool m_inverse; - bool m_exact; -}; - -/** - * @brief A edge collection iterator - * - * The iterator delivers the edges of the edge collection - */ - -class DB_PUBLIC EdgesIterator -{ -public: - typedef db::Edge value_type; - typedef const db::Edge &reference; - typedef const db::Edge *pointer; - typedef std::forward_iterator_tag iterator_category; - typedef void difference_type; - - /** - * @Returns true, if the iterator is at the end - */ - bool at_end () const - { - return m_from == m_to && m_rec_iter.at_end (); - } - - /** - * @brief Increment - */ - EdgesIterator &operator++ () - { - inc (); - set (); - return *this; - } - - /** - * @brief Access - */ - reference operator* () const - { - if (m_rec_iter.at_end ()) { - return *m_from; - } else { - return m_edge; - } - } - - /** - * @brief Access - */ - pointer operator-> () const - { - if (m_rec_iter.at_end ()) { - return &*m_from; - } else { - return &m_edge; - } - } - -private: - friend class Edges; - - typedef db::layer edge_layer_type; - typedef edge_layer_type::iterator iterator_type; - - db::RecursiveShapeIterator m_rec_iter; - db::ICplxTrans m_iter_trans; - db::Edge m_edge; - iterator_type m_from, m_to; - - /** - * @brief ctor from a recursive shape iterator - */ - EdgesIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans), m_from (), m_to () - { - // NOTE: the following initialization appears to be required on some compilers - // (specifically MacOS/clang) to ensure the proper initialization of the iterators - m_from = m_to; - set (); - } - - /** - * @brief ctor from a range of edges inside a vector - */ - EdgesIterator (iterator_type from, iterator_type to) - : m_from (from), m_to (to) - { - // no required yet: set (); - } - - /** - * @brief Establish the iterator at the current position - */ - void set () - { - while (! m_rec_iter.at_end () && ! m_rec_iter.shape ().is_edge ()) { - inc (); - } - if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().edge (m_edge); - m_edge.transform (m_iter_trans * m_rec_iter.trans ()); - } - } - - /** - * @brief Increment the iterator - */ - void inc () - { - if (! m_rec_iter.at_end ()) { - ++m_rec_iter; - } else { - ++m_from; - } - } -}; - -/** - * @brief An edge set - * - * An edge set is basically a collection of edges. They do not necessarily need to form closed contours. - * Edges can be manipulated in various ways. Edge sets closely cooperate with the Region class which is a - * set of polygons. - * - * Edge sets have some methods in common with regions. Edge sets can also be merged, which means that - * edges which are continuations of other edges are joined. - * - * Edge sets can contain degenerated edges. Such edges are some which have identical start and end points. - * Such edges are basically points which have some applications, i.e. as markers for certain locations. - */ - -class DB_PUBLIC Edges +class DB_PUBLIC EdgesDelegate { public: typedef db::Coord coord_type; @@ -311,943 +72,113 @@ public: typedef db::Vector vector_type; typedef db::Point point_type; typedef db::Box box_type; - typedef coord_traits::distance_type distance_type; - typedef db::Edge::distance_type length_type; - typedef EdgesIterator const_iterator; - enum BoolOp { Or, Not, Xor, And }; + typedef coord_traits::distance_type distance_type; + typedef coord_traits::distance_type length_type; - /** - * @brief Default constructor - * - * This constructor creates an empty edge set. - */ - Edges () - : m_edges (false), m_merged_edges (false) - { - init (); - } + EdgesDelegate (); + virtual ~EdgesDelegate (); - /** - * @brief Constructor from an object - * - * Creates a region representing a single instance of that object. - * The object is converted to a polygon and the edges of that polygon are inserted. - */ - template - Edges (const Sh &s) - : m_edges (false), m_merged_edges (false) - { - init (); - insert (s); - } + EdgesDelegate (const EdgesDelegate &other); + EdgesDelegate &operator= (const EdgesDelegate &other); - /** - * @brief Sequence constructor - * - * Creates a region from a sequence of objects. The objects can be edges, boxes, - * polygons, paths or shapes. This version accepts iterators of the begin ... end - * style. - */ - template - Edges (const Iter &b, const Iter &e) - : m_edges (false), m_merged_edges (false) - { - init (); - reserve (e - b); - for (Iter i = b; i != e; ++i) { - insert (*i); - } - } + virtual EdgesDelegate *clone () const = 0; - /** - * @brief Constructor from a RecursiveShapeIterator - * - * Creates a region from a recursive shape iterator. This allows to feed an edge set - * from a hierarchy of cells. - * - * If as_edges is false, only edges will be taken from the recursive shape iterator. - * That is somewhat more efficient since it can avoid a copy in some cases. If as_edges - * is false, shapes will be converted to edges. - */ - Edges (const RecursiveShapeIterator &si, bool as_edges = true); - - /** - * @brief Constructor from a RecursiveShapeIterator with a transformation - * - * Creates a region from a recursive shape iterator. This allows to feed an edge set - * from a hierarchy of cells. The transformation is useful to scale to a specific - * DBU for example. - * - * If as_edges is true, only edges will be taken from the recursive shape iterator. - * That is somewhat more efficient since it can avoid a copy in some cases. If as_edges - * is false, shapes will be converted to edges. - */ - Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges = true, bool merged_semantics = true); - - /** - * @brief Enable progress reporting - * - * @param progress_text The description text of the progress object - */ - void enable_progress (const std::string &progress_desc = std::string ()); - - /** - * @brief Disable progress reporting - */ + void enable_progress (const std::string &progress_desc); void disable_progress (); - /** - * @brief Iterator of the edge set - * - * The iterator delivers the edges of the edge set. - * It follows the at_end semantics. - */ - const_iterator begin () const - { - if (has_valid_edges ()) { - return const_iterator (m_edges.get_layer ().begin (), m_edges.get_layer ().end ()); - } else { - return const_iterator (m_iter, m_iter_trans); - } - } - - /** - * @brief Returns the merged edges if merge semantics applies - * - * If merge semantics is not enabled, this iterator delivers the individual edges. - */ - const_iterator begin_merged () const; - - /** - * @brief Delivers a RecursiveShapeIterator pointing to the edges plus the necessary transformation - */ - std::pair begin_iter () const; - - /** - * @brief Delivers a RecursiveShapeIterator pointing to the merged edges plus the necessary transformation - */ - std::pair begin_merged_iter () const; - - /** - * @brief Insert an edge into the edge set - */ - void insert (const db::Edge &edge); - - /** - * @brief Insert a box into the edge set - * - * This method will insert all edges the box is composed of. - */ - void insert (const db::Box &box); - - /** - * @brief Insert a path into the edge set - * - * This method will insert all edges the path is composed of. - */ - void insert (const db::Path &path); - - /** - * @brief Insert a simple polygon into the edge set - * - * This method will insert all edges the polygon is composed of. - */ - void insert (const db::SimplePolygon &polygon); - - /** - * @brief Insert a polygon into the edge set - * - * This method will insert all edges the polygon is composed of. - */ - void insert (const db::Polygon &polygon); - - /** - * @brief Insert a shape into the region - * - * If the shape is a polygon-type, the shape is converted to a - * polygon and it's edges are inserted into the edge set. - * If the shape is an edge, the edge is inserted into the edge set. - */ - void insert (const db::Shape &shape) - { - if (shape.is_edge ()) { - insert (shape.edge ()); - } else if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - db::Polygon polygon; - shape.polygon (polygon); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - insert (*e); - } - } - } - - /** - * @brief Insert a transformed shape into the edge set - */ - template - void insert (const db::Shape &shape, const T &trans) - { - if (shape.is_edge ()) { - insert (edge_type (trans * shape.edge ())); - } else if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - db::Polygon polygon; - shape.polygon (polygon); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - insert (edge_type (trans * *e)); - } - } - } - - /** - * @brief Returns true if the region is empty - */ - bool empty () const - { - return has_valid_edges () && m_edges.empty (); - } - - /** - * @brief Returns the number of polygons in the region - */ - size_t size () const; - - /** - * @brief Returns a string representing the region - * - * nmax specifies how many polygons are included (set to std::numeric_limits::max() for "all". - */ - std::string to_string (size_t nmax = 10) const; - - /** - * @brief Clear the edge set - */ - void clear (); - - /** - * @brief Reserve memory for the given number of edges - */ - void reserve (size_t n) - { - m_edges.reserve (db::Edge::tag (), n); - } - - /** - * @brief Sets the merged-semantics flag - * - * If merged semantics is enabled (the default), coherent polygons will be considered - * as single regions and artificial edges such as cut-lines will not be considered. - * Merged semantics thus is equivalent to considering coherent areas rather than - * single polygons. - */ void set_merged_semantics (bool f); - - /** - * @brief Gets the merged-semantics flag - */ bool merged_semantics () const { return m_merged_semantics; } - /** - * @brief Returns true if the region is merged - */ - bool is_merged () const + void set_strict_handling (bool f); + bool strict_handling () const { - return m_is_merged; + return m_strict_handling; } - /** - * @brief Returns the total length of the edges - * Merged semantics applies. In merged semantics, the length is the correct total length of the edges. - * Without merged semantics, overlapping parts are counted twice. - * - * If a box is given, the computation is restricted to that box. - * Edges coincident with the box edges are counted only if the form outer edges at the box edge. - */ - length_type length (const db::Box &box = db::Box ()) const; + virtual std::string to_string (size_t nmax) const = 0; - /** - * @brief Returns the bounding box of the region - */ - Box bbox () const + virtual EdgesIteratorDelegate *begin () const = 0; + virtual EdgesIteratorDelegate *begin_merged () const = 0; + + virtual std::pair begin_iter () const = 0; + virtual std::pair begin_merged_iter () const = 0; + + virtual bool empty () const = 0; + virtual bool is_merged () const = 0; + virtual size_t size () const = 0; + + virtual distance_type length (const db::Box &box) const = 0; + virtual Box bbox () const = 0; + + virtual EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs enclosing_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs overlap_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs separation_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs inside_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + + virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter) = 0; + virtual EdgesDelegate *filtered (const EdgeFilterBase &filter) const = 0; + + virtual EdgesDelegate *merged_in_place () = 0; + virtual EdgesDelegate *merged () const = 0; + + virtual EdgesDelegate *and_with (const Edges &other) const = 0; + virtual EdgesDelegate *and_with (const Region &other) const = 0; + virtual EdgesDelegate *not_with (const Edges &other) const = 0; + virtual EdgesDelegate *not_with (const Region &other) const = 0; + virtual EdgesDelegate *xor_with (const Edges &other) const = 0; + virtual EdgesDelegate *or_with (const Edges &other) const = 0; + virtual EdgesDelegate *add_in_place (const Edges &other) = 0; + virtual EdgesDelegate *add (const Edges &other) const = 0; + + virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const = 0; + virtual EdgesDelegate *start_segments (length_type length, double fraction) const = 0; + virtual EdgesDelegate *end_segments (length_type length, double fraction) const = 0; + virtual EdgesDelegate *centers (length_type length, double fraction) const = 0; + + virtual EdgesDelegate *inside_part (const Region &other) const = 0; + virtual EdgesDelegate *outside_part (const Region &other) const = 0; + virtual EdgesDelegate *selected_interacting (const Region &other) const = 0; + virtual EdgesDelegate *selected_not_interacting (const Region &other) const = 0; + virtual EdgesDelegate *selected_interacting (const Edges &other) const = 0; + virtual EdgesDelegate *selected_not_interacting (const Edges &other) const = 0; + + virtual EdgesDelegate *in (const Edges &other, bool invert) const = 0; + + virtual const db::Edge *nth (size_t n) const = 0; + virtual bool has_valid_edges () const = 0; + virtual bool has_valid_merged_edges () const = 0; + + virtual const db::RecursiveShapeIterator *iter () const = 0; + + virtual bool equals (const Edges &other) const = 0; + virtual bool less (const Edges &other) const = 0; + +protected: + const std::string &progress_desc () const { - ensure_bbox_valid (); - return m_bbox; + return m_progress_desc; } - /** - * @brief Filters the edge set - * - * This method will keep all edges for which the filter returns true. - * Merged semantics applies. - */ - Edges &filter (EdgeFilterBase &filter) + bool report_progress () const { - edge_iterator_type ew = m_edges.get_layer ().begin (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - if (filter.selected (*e)) { - if (ew == m_edges.get_layer ().end ()) { - m_edges.get_layer ().insert (*e); - ew = m_edges.get_layer ().end (); - } else { - m_edges.get_layer ().replace (ew++, *e); - } - } - } - m_edges.get_layer ().erase (ew, m_edges.get_layer ().end ()); - m_merged_edges.clear (); - m_is_merged = m_merged_semantics; - m_iter = db::RecursiveShapeIterator (); - return *this; + return m_report_progress; } - /** - * @brief Returns the filtered edges - * - * This method will return a new region with only those edges which - * conform to the filter criterion. - * Merged semantics applies. - */ - Edges filtered (const EdgeFilterBase &filter) const - { - Edges d; - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - if (filter.selected (*e)) { - d.insert (*e); - } - } - return d; - } - - /** - * @brief Returns all edges found in the other edge collection as well - * - * If "invert" is true, all edges not found in the other collection - * are returned. - */ - Edges in (const Edges &other, bool invert) const; - - /** - * @brief Transform the edge set - */ - template - Edges &transform (const T &trans) - { - if (! trans.is_unity ()) { - ensure_valid_edges (); - for (edge_iterator_type e = m_edges.get_layer ().begin (); e != m_edges.get_layer ().end (); ++e) { - m_edges.get_layer ().replace (e, e->transformed (trans)); - } - m_iter_trans = db::ICplxTrans (trans) * m_iter_trans; - m_bbox_valid = false; - } - return *this; - } - - /** - * @brief Returns the transformed edge set - */ - template - Edges transformed (const T &trans) const - { - Edges d (*this); - d.transform (trans); - return d; - } - - /** - * @brief Swap with the other region - */ - void swap (db::Edges &other); - - /** - * @brief returns the extended edges - * - * Edges are extended by creating a rectangle on each edge. The rectangle is constructed on the - * edge by applying the extensions given by ext_o, ext_b, ext_e, ext_i at the outside, the - * beginning, the end and the inside. - * If the edge is laid flat pointing from left to right, the outside is at the top, the inside - * is at the bottom. - * - * For degenerated edges with length 0, the orientation is assumed to the horizontal. The extended - * method creates a rectangle with specified dimensions: ext_b to the left, ext_o to the top, ext_i to the bottom - * and ext_e to the right. - * - * If the joined parameter is set to true, adjacent edges are joined before the extension is applied. - * A the join points, the extension is created similar to what the sizing function does. - * - * Note: the output is given as an out parameter since because of the include hierarchy we can't use - * Region as a return value directly. - * - * Merged semantics applies. - */ - void extended (Region &output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join = false) const; - - /** - * @brief Returns edges (point-like) representing the start part of the edges - * - * The length of the part can be choosen by length or a fraction of the original length. - * If length and fraction are 0, a point at the beginning of the edge will be created. - * If length is non-zero and fraction is 0, a segment in the direction of the edge - * with the given length is created, even if the length is larger than the original - * edge. - * - * If fraction is given and length is 0, the segment will have a length which is the specified - * fraction of the original edge. - * If both values are given, the resulting edge will have a length which is the maximum of - * both the fixed length and the length derived from the fraction. - * - * Length and fraction can be negative in which case the resulting segment will point - * in the opposite direction of the original edge. - * - * Merged semantics applies. - */ - Edges start_segments (length_type length, double fraction) const; - - /** - * @brief Returns edges (point-like) representing the end part of the edges - * - * This method behaves similar to \start_segments but creates segments at the end of - * the edges. - * - * Merged semantics applies. - */ - Edges end_segments (length_type length, double fraction) const; - - /** - * @brief Returns edges (point-like) representing the center of the edges - * - * This method behaves similar to \start_segments but creates segments at the centers of - * the edges. - * - * Merged semantics applies. - */ - Edges centers (length_type length, double fraction) const; - - /** - * @brief Boolean AND operator - * - * This operation returns the parts of the edges which coincide with edges from "other" - * After this operation the edges are not necessarily merged. - */ - Edges operator& (const Edges &other) const - { - return boolean (&other, And); - } - - /** - * @brief In-place boolean AND operator - */ - Edges &operator&= (const Edges &other) - { - inplace_boolean (&other, And); - return *this; - } - - /** - * @brief Boolean AND operator with a region - * - * This operation returns the parts of the edges which are inside the given region. - * Edges on the borders of the polygons are included in the edge set. - * As a side effect, the edges are made non-intersecting by introducing cut points where - * edges intersect. - */ - Edges operator& (const Region &other) const - { - Edges d (*this); - d &= other; - return d; - } - - /** - * @brief In-place boolean AND operator with a region - */ - Edges &operator&= (const Region &other) - { - edge_region_op (other, false /*inside*/, true /*include borders*/); - return *this; - } - - /** - * @brief Boolean NOT operator - * - * This operation returns the parts of the edges which do not coincide with edges from "other" - * After this operation the edges are not necessarily merged. - */ - Edges operator- (const Edges &other) const - { - return boolean (&other, Not); - } - - /** - * @brief In-place boolean NOT operator - */ - Edges &operator-= (const Edges &other) - { - inplace_boolean (&other, Not); - return *this; - } - - /** - * @brief Boolean NOT operator with a region - * - * This operation returns the parts of the edges which are outside the given region. - * Edges on the borders of the polygons are removed from the edge set. - * As a side effect, the edges are made non-intersecting by introducing cut points where - * edges intersect. - */ - Edges operator- (const Region &other) const - { - Edges d (*this); - d -= other; - return d; - } - - /** - * @brief In-place boolean NOT operator with a region - */ - Edges &operator-= (const Region &other) - { - edge_region_op (other, true /*outside*/, true /*include borders*/); - return *this; - } - - /** - * @brief Boolean XOR operator - * - * This operation returns the parts of the edges which do not coincide with edges from "other" - * and vice versa. - * After this operation the edges are not necessarily merged. - */ - Edges operator^ (const Edges &other) const - { - return boolean (&other, Xor); - } - - /** - * @brief In-place boolean XOR operator - */ - Edges &operator^= (const Edges &other) - { - inplace_boolean (&other, Xor); - return *this; - } - - /** - * @brief Joining of edge sets - * - * This method will combine the edges from "other" with the egdes of "this". - * After this operation the edges are not necessarily merged. - */ - Edges operator+ (const Edges &other) const - { - Edges d (*this); - d += other; - return d; - } - - /** - * @brief In-place joining of edge sets - */ - Edges &operator+= (const Edges &other); - - /** - * @brief Boolean OR operator - * - * This method will combine the edges from "other" with the egdes of "this". - * After this operation the edges are usually merged. - */ - Edges operator| (const Edges &other) const - { - return boolean (&other, Or); - } - - /** - * @brief In-place boolean OR operator - */ - Edges &operator|= (const Edges &other) - { - inplace_boolean (&other, Or); - return *this; - } - - /** - * @brief Select the edges inside the given region - * - * This method will select the edges inside the given region. - * Edges on the border of the region won't be selected. - * As a side effect, the edges are made non-intersecting by introducing cut points where - * edges intersect. - */ - Edges &select_inside_part (const Region &other) - { - edge_region_op (other, false /*inside*/, false /*don't include borders*/); - return *this; - } - - /** - * @brief Returns the edges inside the given region - * - * This is an out-of-place version of "select_inside_part". - */ - Edges inside_part (const Region &other) const - { - Edges d (*this); - d.select_inside_part (other); - return d; - } - - /** - * @brief Select the edge parts outside of the given region - * - * This method will select the edge parts outside of the given region. - * Edges on the border of the region won't be selected. - * As a side effect, the edges are made non-intersecting by introducing cut points where - * edges intersect. - */ - Edges &select_outside_part (const Region &other) - { - edge_region_op (other, true /*outside*/, false /*don't include borders*/); - return *this; - } - - /** - * @brief Returns the edge parts outside of the given region - * - * This is an out-of-place version of "select_outside_part". - */ - Edges outside_part (const Region &other) const - { - Edges d (*this); - d.select_outside_part (other); - return d; - } - - /** - * @brief Selects all edges of this edge set which overlap or touch with polygons from the region - * - * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be - * selected as a whole. - */ - Edges &select_interacting (const Region &other); - - /** - * @brief Returns all edges of this edge set which overlap or touch with polygons from the region - * - * This method is an out-of-place version of select_interacting. - */ - Edges selected_interacting (const Region &other) const - { - Edges d (*this); - d.select_interacting (other); - return d; - } - - /** - * @brief Selects all edges of this edge set which do not overlap or touch with polygons from the region - * - * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be - * selected as a whole. - */ - Edges &select_not_interacting (const Region &other); - - /** - * @brief Returns all edges of this edge set which do not overlap or touch with polygons from the region - * - * This method is an out-of-place version of select_not_interacting. - */ - Edges selected_not_interacting (const Region &other) const - { - Edges d (*this); - d.select_not_interacting (other); - return d; - } - - /** - * @brief Selects all edges of this edge set which overlap or touch with edges from the other edge set - * - * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be - * selected as a whole. - */ - Edges &select_interacting (const Edges &other); - - /** - * @brief Returns all edges of this edge set which overlap or touch with edges from the other edge set - * - * This method is an out-of-place version of select_interacting. - */ - Edges selected_interacting (const Edges &other) const - { - Edges d (*this); - d.select_interacting (other); - return d; - } - - /** - * @brief Selects all edges of this edge set which do not overlap or touch with edges from the other edge set - * - * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be - * selected as a whole. - */ - Edges &select_not_interacting (const Edges &other); - - /** - * @brief Returns all edges of this edge set which do not overlap or touch with edges from the other edge set - * - * This method is an out-of-place version of select_not_interacting. - */ - Edges selected_not_interacting (const Edges &other) const - { - Edges d (*this); - d.select_not_interacting (other); - return d; - } - - /** - * @brief Merge the edge set - * - * This method merges the edges of the edge set if they are not merged already. - * It returns a reference to this edge set. - * Edges are merged by joining them if one edge is a continuation of another. - * An out-of-place merge version is "merged". - */ - Edges &merge () - { - if (! is_merged ()) { - inplace_boolean (0, Or); - } - return *this; - } - - /* - * @brief Returns the merged edge set - * - * This is the out-of-place merge. It returns a new edge set but does not modify - * the edge set it is called on. An in-place version is "merge". - */ - Edges merged () const - { - return boolean (0, Or); - } - - /** - * @brief Applies a width check and returns EdgePairs which correspond to violation markers - * - * The width check will create a edge pairs if the width of the area between the - * edges is less than the specified threshold d. Without "whole_edges", the parts of - * the edges are returned which violate the condition. If "whole_edges" is true, the - * result will contain the complete edges participating in the result. - * - * "Width" refers to the space between the "inside" sides of the edges. - * - * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" - * metrics are available. - * - * ignore_angle allows specification of a maximum angle the edges can have to not participate - * in the check. By choosing 90 degree, edges having an angle of 90 degree and larger are not checked, - * but acute corners are for example. - * - * With min_projection and max_projection it is possible to specify how edges must be related - * to each other. If the length of the projection of either edge on the other is >= min_projection - * or < max_projection, the edges are considered for the check. - * - * The order of the edges in the resulting edge pairs is undefined. - * - * Merged semantics applies. - */ - EdgePairs width_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::WidthRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a space check and returns EdgePairs which correspond to violation markers - * - * "Space" refers to the space between the "outside" sides of the edges. - * - * For the parameters see \width_check. The space check reports edges for which the space is - * less than the specified threshold d. - * - * Merged semantics applies. - */ - EdgePairs space_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::SpaceRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies an enclosing check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "inside" side of the edge from this edge set, the orientation is parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs enclosing_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::OverlapRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies an overlap check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "inside" side of the edge from this edge set, the orientation is anti-parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs overlap_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::WidthRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies an separation check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "outside" side of the edge from this edge set, the orientation is anti-parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs separation_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::SpaceRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a inside check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "outide" side of the edge from this edge set, the orientation is parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs inside_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::InsideRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Returns the nth edge - * - * This method will force the edges to be inside the edge vector and will invalidate any iterator. - * If that happens, the method may be costly. - * The iterator should be used whenever possible. - */ - const db::Edge *nth (size_t n) const - { - ensure_valid_edges (); - return n < m_edges.size () ? &m_edges.get_layer ().begin () [n] : 0; - } - - /** - * @brief Returns true, if the edge set has valid edges stored within itself - */ - bool has_valid_edges () const - { - return m_iter.at_end (); - } - - /** - * @brief Ensures the edge collection has valid edges - * - * This method is const since it has const semantics. - */ - void ensure_valid_edges () const; - - /** - * @brief Ensures the edge collection has valid merged edges - * - * It will make sure that begin_merged will deliver an - * iterator to an edge with a unique memory location. - */ - void ensure_valid_merged_edges () const; - - /** - * @brief Equality - */ - bool operator== (const db::Edges &other) const; - - /** - * @brief Inequality - */ - bool operator!= (const db::Edges &other) const - { - return !operator== (other); - } - - /** - * @brief Less operator - */ - bool operator< (const db::Edges &other) const; + virtual void merged_semantics_changed () { } private: - typedef db::layer edge_layer_type; - typedef edge_layer_type::iterator edge_iterator_type; - - bool m_is_merged; bool m_merged_semantics; - mutable db::Shapes m_edges; - mutable db::Shapes m_merged_edges; - mutable db::Box m_bbox; - mutable bool m_bbox_valid; - mutable bool m_merged_edges_valid; - mutable db::RecursiveShapeIterator m_iter; - db::ICplxTrans m_iter_trans; + bool m_strict_handling; bool m_report_progress; std::string m_progress_desc; - - void init (); - void invalidate_cache (); - void set_valid_edges (); - void ensure_bbox_valid () const; - void ensure_merged_edges_valid () const; - EdgePairs run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - void inplace_boolean (const Edges *other, BoolOp op); - Edges boolean (const Edges *other, BoolOp op) const; - void edge_region_op (const Region &other, bool outside, bool include_borders); }; -} // namespace db - -namespace tl -{ - /** - * @brief The type traits for the edges type - */ - template <> - struct type_traits : public type_traits - { - typedef true_tag supports_extractor; - typedef true_tag supports_to_string; - typedef true_tag has_less_operator; - typedef true_tag has_equal_operator; - }; - } #endif -#endif - diff --git a/src/db/db/dbEmptyEdges.cc b/src/db/db/dbEmptyEdges.cc new file mode 100644 index 000000000..f8f8174a0 --- /dev/null +++ b/src/db/db/dbEmptyEdges.cc @@ -0,0 +1,105 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbEmptyEdges.h" +#include "dbEmptyRegion.h" +#include "dbEdges.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// EmptyEdges implementation + +EmptyEdges::EmptyEdges () +{ + // .. nothing yet .. +} + +EmptyEdges::~EmptyEdges () +{ + // .. nothing yet .. +} + +EmptyEdges::EmptyEdges (const EmptyEdges &other) + : EdgesDelegate (other) +{ + // .. nothing yet .. +} + +EdgesDelegate * +EmptyEdges::clone () const +{ + return new EmptyEdges (*this); +} + +RegionDelegate * +EmptyEdges::extended (coord_type, coord_type, coord_type, coord_type, bool) const +{ + return new EmptyRegion (); +} + +EdgesDelegate * +EmptyEdges::add_in_place (const Edges &other) +{ + return add (other); +} + +EdgesDelegate * +EmptyEdges::add (const Edges &other) const +{ + return other.delegate ()->clone (); +} + +EdgesDelegate * +EmptyEdges::xor_with (const Edges &other) const +{ + return or_with (other); +} + +EdgesDelegate * +EmptyEdges::or_with (const Edges &other) const +{ + if (other.empty ()) { + return new EmptyEdges (); + } else if (! other.strict_handling ()) { + return other.delegate ()->clone (); + } else { + return other.delegate ()->merged (); + } +} + +bool +EmptyEdges::equals (const Edges &other) const +{ + return other.empty (); +} + +bool +EmptyEdges::less (const Edges &other) const +{ + return other.empty () ? false : true; +} + +} + diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h new file mode 100644 index 000000000..21833d08f --- /dev/null +++ b/src/db/db/dbEmptyEdges.h @@ -0,0 +1,111 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbEmptyEdges +#define HDR_dbEmptyEdges + +#include "dbCommon.h" +#include "dbEdgesDelegate.h" +#include "dbRecursiveShapeIterator.h" + +namespace db { + +/** + * @brief An empty Edges + */ +class DB_PUBLIC EmptyEdges + : public EdgesDelegate +{ +public: + EmptyEdges (); + virtual ~EmptyEdges (); + + EmptyEdges (const EmptyEdges &other); + EdgesDelegate *clone () const; + + virtual EdgesIteratorDelegate *begin () const { return 0; } + virtual EdgesIteratorDelegate *begin_merged () const { return 0; } + + virtual std::pair begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + virtual std::pair begin_merged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + + virtual bool empty () const { return true; } + virtual size_t size () const { return 0; } + virtual std::string to_string (size_t) const { return std::string (); } + virtual bool is_merged () const { return true; } + virtual distance_type length (const db::Box &) const { return 0; } + virtual Box bbox () const { return db::Box (); } + + virtual EdgePairs width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs enclosing_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs overlap_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs separation_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs inside_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + + virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &) { return this; } + virtual EdgesDelegate *filtered (const EdgeFilterBase &) const { return new EmptyEdges (); } + + virtual EdgesDelegate *merged_in_place () { return this; } + virtual EdgesDelegate *merged () const { return new EmptyEdges (); } + + virtual EdgesDelegate *and_with (const Edges &) const { return new EmptyEdges (); } + virtual EdgesDelegate *and_with (const Region &) const { return new EmptyEdges (); } + virtual EdgesDelegate *not_with (const Edges &) const { return new EmptyEdges (); } + virtual EdgesDelegate *not_with (const Region &) const { return new EmptyEdges (); } + virtual EdgesDelegate *xor_with (const Edges &other) const; + virtual EdgesDelegate *or_with (const Edges &other) const; + virtual EdgesDelegate *add_in_place (const Edges &other); + virtual EdgesDelegate *add (const Edges &other) const; + + virtual RegionDelegate *extended (coord_type, coord_type, coord_type, coord_type, bool) const; + virtual EdgesDelegate *start_segments (length_type, double) const { return new EmptyEdges (); } + virtual EdgesDelegate *end_segments (length_type, double) const { return new EmptyEdges (); } + virtual EdgesDelegate *centers (length_type, double) const { return new EmptyEdges (); } + + virtual EdgesDelegate *inside_part (const Region &) const { return new EmptyEdges (); } + virtual EdgesDelegate *outside_part (const Region &) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_interacting (const Edges &) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_not_interacting (const Edges &) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_interacting (const Region &) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_not_interacting (const Region &) const { return new EmptyEdges (); } + + virtual EdgesDelegate *in (const Edges &, bool) const { return new EmptyEdges (); } + + virtual const db::Edge *nth (size_t) const { tl_assert (false); } + virtual bool has_valid_edges () const { return true; } + virtual bool has_valid_merged_edges () const { return true; } + + virtual const db::RecursiveShapeIterator *iter () const { return 0; } + + virtual bool equals (const Edges &other) const; + virtual bool less (const Edges &other) const; + +private: + EmptyEdges &operator= (const EmptyEdges &other); +}; + +} // namespace db + +#endif + diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc new file mode 100644 index 000000000..087b73368 --- /dev/null +++ b/src/db/db/dbFlatEdges.cc @@ -0,0 +1,363 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbFlatEdges.h" +#include "dbEmptyEdges.h" +#include "dbEdgeBoolean.h" +#include "dbEdges.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// FlatEdges implementation + +FlatEdges::FlatEdges () + : AsIfFlatEdges (), m_edges (false), m_merged_edges (false) +{ + init (); +} + +FlatEdges::~FlatEdges () +{ + // .. nothing yet .. +} + +FlatEdges::FlatEdges (const FlatEdges &other) + : AsIfFlatEdges (other), m_edges (false), m_merged_edges (false) +{ + init (); + + m_is_merged = other.m_is_merged; + m_edges = other.m_edges; + m_merged_edges = other.m_merged_edges; + m_merged_edges_valid = other.m_merged_edges_valid; +} + +FlatEdges::FlatEdges (const db::Shapes &edges, bool is_merged) + : AsIfFlatEdges (), m_edges (edges), m_merged_edges (false) +{ + init (); + + m_is_merged = is_merged; +} + +FlatEdges::FlatEdges (bool is_merged) + : AsIfFlatEdges (), m_edges (false), m_merged_edges (false) +{ + init (); + + m_is_merged = is_merged; +} + +void FlatEdges::set_is_merged (bool m) +{ + m_is_merged = m; +} + +void FlatEdges::invalidate_cache () +{ + invalidate_bbox (); + m_merged_edges.clear (); + m_merged_edges_valid = false; +} + +void FlatEdges::init () +{ + m_is_merged = true; + m_merged_edges_valid = false; +} + +void FlatEdges::merged_semantics_changed () +{ + m_merged_edges.clear (); + m_merged_edges_valid = false; +} + +void FlatEdges::reserve (size_t n) +{ + m_edges.reserve (db::Polygon::tag (), n); +} + +void +FlatEdges::ensure_merged_edges_valid () const +{ + if (! m_merged_edges_valid) { + + m_merged_edges.clear (); + + db::Shapes tmp; + EdgeBooleanClusterCollector cluster_collector (&tmp, EdgeOr); + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (m_edges.size ()); + + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (&*e, 0); + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + m_merged_edges.swap (tmp); + m_merged_edges_valid = true; + + } +} + +EdgesIteratorDelegate *FlatEdges::begin () const +{ + return new FlatEdgesIterator (m_edges.get_layer ().begin (), m_edges.get_layer ().end ()); +} + +EdgesIteratorDelegate *FlatEdges::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_edges_valid (); + return new FlatEdgesIterator (m_merged_edges.get_layer ().begin (), m_merged_edges.get_layer ().end ()); + } +} + +std::pair FlatEdges::begin_iter () const +{ + return std::make_pair (db::RecursiveShapeIterator (m_edges), db::ICplxTrans ()); +} + +std::pair FlatEdges::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_edges_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_edges), db::ICplxTrans ()); + } +} + +bool FlatEdges::empty () const +{ + return m_edges.empty (); +} + +size_t FlatEdges::size () const +{ + return m_edges.size (); +} + +bool FlatEdges::is_merged () const +{ + return m_is_merged; +} + +Box FlatEdges::compute_bbox () const +{ + m_edges.update_bbox (); + return m_edges.bbox (); +} + +EdgesDelegate * +FlatEdges::filter_in_place (const EdgeFilterBase &filter) +{ + edge_iterator_type pw = m_edges.get_layer ().begin (); + for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + if (pw == m_edges.get_layer ().end ()) { + m_edges.get_layer ().insert (*p); + pw = m_edges.get_layer ().end (); + } else { + m_edges.get_layer ().replace (pw++, *p); + } + } + } + + m_edges.get_layer ().erase (pw, m_edges.get_layer ().end ()); + m_merged_edges.clear (); + m_is_merged = merged_semantics (); + + return this; +} + +EdgesDelegate *FlatEdges::add (const Edges &other) const +{ + std::auto_ptr new_region (new FlatEdges (*this)); + new_region->invalidate_cache (); + new_region->set_is_merged (false); + + FlatEdges *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + new_region->raw_edges ().insert (other_flat->raw_edges ().get_layer ().begin (), other_flat->raw_edges ().get_layer ().end ()); + + } else { + + size_t n = new_region->raw_edges ().size (); + for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + new_region->raw_edges ().reserve (db::Edge::tag (), n); + + for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { + new_region->raw_edges ().insert (*p); + } + + } + + return new_region.release (); +} + +EdgesDelegate *FlatEdges::add_in_place (const Edges &other) +{ + invalidate_cache (); + m_is_merged = false; + + FlatEdges *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + m_edges.insert (other_flat->raw_edges ().get_layer ().begin (), other_flat->raw_edges ().get_layer ().end ()); + + } else { + + size_t n = m_edges.size (); + for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + m_edges.reserve (db::Edge::tag (), n); + + for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { + m_edges.insert (*p); + } + + } + + return this; +} + +const db::Edge *FlatEdges::nth (size_t n) const +{ + return n < m_edges.size () ? &m_edges.get_layer ().begin () [n] : 0; +} + +bool FlatEdges::has_valid_edges () const +{ + return true; +} + +bool FlatEdges::has_valid_merged_edges () const +{ + return true; +} + +const db::RecursiveShapeIterator *FlatEdges::iter () const +{ + return 0; +} + +void +FlatEdges::insert (const db::Box &box) +{ + if (! box.empty () && box.width () > 0 && box.height () > 0) { + + bool was_empty = empty (); + + m_edges.insert (db::Edge (box.lower_left (), box.upper_left ())); + m_edges.insert (db::Edge (box.upper_left (), box.upper_right ())); + m_edges.insert (db::Edge (box.upper_right (), box.lower_right ())); + m_edges.insert (db::Edge (box.lower_right (), box.lower_left ())); + + if (was_empty) { + + m_is_merged = true; + update_bbox (box); + + } else { + + m_is_merged = false; + invalidate_cache (); + + } + + } +} + +void +FlatEdges::insert (const db::Path &path) +{ + if (path.points () > 0) { + insert (path.polygon ()); + } +} + +void +FlatEdges::insert (const db::Polygon &polygon) +{ + if (polygon.holes () > 0 || polygon.vertices () > 0) { + for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + m_edges.insert (*e); + } + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatEdges::insert (const db::SimplePolygon &polygon) +{ + if (polygon.vertices () > 0) { + for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + m_edges.insert (*e); + } + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatEdges::insert (const db::Edge &edge) +{ + m_edges.insert (edge); +} + +void +FlatEdges::insert (const db::Shape &shape) +{ + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + + db::Polygon poly; + shape.polygon (poly); + insert (poly); + + } else if (shape.is_edge ()) { + + db::Edge edge; + shape.edge (edge); + insert (edge); + + } +} + +} + diff --git a/src/db/db/dbFlatEdges.h b/src/db/db/dbFlatEdges.h new file mode 100644 index 000000000..8a034f41f --- /dev/null +++ b/src/db/db/dbFlatEdges.h @@ -0,0 +1,203 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbFlatEdges +#define HDR_dbFlatEdges + +#include "dbCommon.h" + +#include "dbAsIfFlatEdges.h" +#include "dbShapes.h" +#include "dbShapes2.h" + +namespace db { + +/** + * @brief An iterator delegate for the flat region + */ +class DB_PUBLIC FlatEdgesIterator + : public EdgesIteratorDelegate +{ +public: + typedef db::layer edge_layer_type; + typedef edge_layer_type::iterator iterator_type; + + FlatEdgesIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // .. nothing yet .. + } + + virtual bool at_end () const + { + return m_from == m_to; + } + + virtual void increment () + { + ++m_from; + } + + virtual const value_type *get () const + { + return m_from.operator-> (); + } + + virtual EdgesIteratorDelegate *clone () const + { + return new FlatEdgesIterator (*this); + } + +private: + friend class Edges; + + iterator_type m_from, m_to; +}; + +/** + * @brief A flat, edge-set delegate + */ +class DB_PUBLIC FlatEdges + : public AsIfFlatEdges +{ +public: + typedef db::layer edge_layer_type; + typedef edge_layer_type::iterator edge_iterator_type; + + FlatEdges (); + FlatEdges (const db::Shapes &edges, bool is_merged); + FlatEdges (bool is_merged); + + FlatEdges (const FlatEdges &other); + + virtual ~FlatEdges (); + + EdgesDelegate *clone () const + { + return new FlatEdges (*this); + } + + void reserve (size_t); + + virtual EdgesIteratorDelegate *begin () const; + virtual EdgesIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + virtual bool is_merged () const; + + virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter); + + virtual EdgesDelegate *add_in_place (const Edges &other); + virtual EdgesDelegate *add (const Edges &other) const; + + virtual const db::Edge *nth (size_t n) const; + virtual bool has_valid_edges () const; + virtual bool has_valid_merged_edges () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + void insert (const db::Box &box); + void insert (const db::Path &path); + void insert (const db::SimplePolygon &polygon); + void insert (const db::Polygon &polygon); + void insert (const db::Edge &edge); + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + + db::Polygon poly; + shape.polygon (poly); + poly.transform (trans); + insert (poly); + + } else if (shape.is_edge ()) { + + db::Edge edge; + shape.edge (edge); + edge.transform (trans); + insert (edge); + + } + } + + template + void insert (const Iter &b, const Iter &e) + { + reserve (size () + (e - b)); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } + + template + void transform (const Trans &trans) + { + if (! trans.is_unity ()) { + for (edge_iterator_type p = m_edges.template get_layer ().begin (); p != m_edges.get_layer ().end (); ++p) { + m_edges.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } + +protected: + virtual void merged_semantics_changed (); + virtual Box compute_bbox () const; + void invalidate_cache (); + void set_is_merged (bool m); + +private: + friend class AsIfFlatEdges; + + db::Shapes &raw_edges () { return m_edges; } + + FlatEdges &operator= (const FlatEdges &other); + + bool m_is_merged; + mutable db::Shapes m_edges; + mutable db::Shapes m_merged_edges; + mutable bool m_merged_edges_valid; + + void init (); + void ensure_merged_edges_valid () const; +}; + +} + +#endif + diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc new file mode 100644 index 000000000..47054ecc5 --- /dev/null +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -0,0 +1,268 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbOriginalLayerEdges.h" +#include "dbFlatEdges.h" +#include "dbEdges.h" +#include "dbEdgeBoolean.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// OriginalLayerEdges implementation + +namespace +{ + + class OriginalLayerEdgesIterator + : public EdgesIteratorDelegate + { + public: + OriginalLayerEdgesIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) + : m_rec_iter (iter), m_iter_trans (trans) + { + set (); + } + + virtual bool at_end () const + { + return m_rec_iter.at_end (); + } + + virtual void increment () + { + inc (); + set (); + } + + virtual const value_type *get () const + { + return &m_edge; + } + + virtual EdgesIteratorDelegate *clone () const + { + return new OriginalLayerEdgesIterator (*this); + } + + private: + friend class Edges; + + db::RecursiveShapeIterator m_rec_iter; + db::ICplxTrans m_iter_trans; + db::Edge m_edge; + + void set () + { + while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_edge () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { + ++m_rec_iter; + } + if (! m_rec_iter.at_end ()) { + m_rec_iter.shape ().edge (m_edge); + m_edge.transform (m_iter_trans * m_rec_iter.trans ()); + } + } + + void inc () + { + if (! m_rec_iter.at_end ()) { + ++m_rec_iter; + } + } + }; + +} + +OriginalLayerEdges::OriginalLayerEdges () + : AsIfFlatEdges (), m_merged_edges (false) +{ + init (); +} + +OriginalLayerEdges::OriginalLayerEdges (const OriginalLayerEdges &other) + : AsIfFlatEdges (other), + m_is_merged (other.m_is_merged), + m_merged_edges (other.m_merged_edges), + m_merged_edges_valid (other.m_merged_edges_valid), + m_iter (other.m_iter), + m_iter_trans (other.m_iter_trans) +{ + // .. nothing yet .. +} + +OriginalLayerEdges::OriginalLayerEdges (const RecursiveShapeIterator &si, bool is_merged) + : AsIfFlatEdges (), m_merged_edges (false), m_iter (si) +{ + init (); + + m_is_merged = is_merged; +} + +OriginalLayerEdges::OriginalLayerEdges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) + : AsIfFlatEdges (), m_merged_edges (false), m_iter (si), m_iter_trans (trans) +{ + init (); + + m_is_merged = is_merged; + set_merged_semantics (merged_semantics); +} + +OriginalLayerEdges::~OriginalLayerEdges () +{ + // .. nothing yet .. +} + +EdgesDelegate * +OriginalLayerEdges::clone () const +{ + return new OriginalLayerEdges (*this); +} + +EdgesIteratorDelegate * +OriginalLayerEdges::begin () const +{ + return new OriginalLayerEdgesIterator (m_iter, m_iter_trans); +} + +EdgesIteratorDelegate * +OriginalLayerEdges::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_edges_valid (); + return new FlatEdgesIterator (m_merged_edges.get_layer ().begin (), m_merged_edges.get_layer ().end ()); + } +} + +std::pair +OriginalLayerEdges::begin_iter () const +{ + return std::make_pair (m_iter, m_iter_trans); +} + +std::pair +OriginalLayerEdges::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_edges_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_edges), db::ICplxTrans ()); + } +} + +bool +OriginalLayerEdges::empty () const +{ + return m_iter.at_end (); +} + +bool +OriginalLayerEdges::is_merged () const +{ + return m_is_merged; +} + +const db::Edge * +OriginalLayerEdges::nth (size_t) const +{ + tl_assert (false); +} + +bool +OriginalLayerEdges::has_valid_edges () const +{ + return false; +} + +bool +OriginalLayerEdges::has_valid_merged_edges () const +{ + return merged_semantics () && ! m_is_merged; +} + +const db::RecursiveShapeIterator * +OriginalLayerEdges::iter () const +{ + return &m_iter; +} + +bool +OriginalLayerEdges::equals (const Edges &other) const +{ + const OriginalLayerEdges *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return true; + } else { + return AsIfFlatEdges::equals (other); + } +} + +bool +OriginalLayerEdges::less (const Edges &other) const +{ + const OriginalLayerEdges *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return false; + } else { + return AsIfFlatEdges::less (other); + } +} + +void +OriginalLayerEdges::init () +{ + m_is_merged = true; + m_merged_edges_valid = false; +} + +void +OriginalLayerEdges::ensure_merged_edges_valid () const +{ + if (! m_merged_edges_valid) { + + m_merged_edges.clear (); + + db::Shapes tmp; + EdgeBooleanClusterCollector cluster_collector (&tmp, EdgeOr); + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size ()); + + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (&*e, 0); + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + m_merged_edges.swap (tmp); + m_merged_edges_valid = true; + + } +} + +} diff --git a/src/db/db/dbOriginalLayerEdges.h b/src/db/db/dbOriginalLayerEdges.h new file mode 100644 index 000000000..19f0ab241 --- /dev/null +++ b/src/db/db/dbOriginalLayerEdges.h @@ -0,0 +1,85 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbOriginalLayerEdges +#define HDR_dbOriginalLayerEdges + +#include "dbCommon.h" + +#include "dbAsIfFlatEdges.h" +#include "dbShapes.h" +#include "dbRecursiveShapeIterator.h" + +namespace db { + +/** + * @brief An original layerregion based on a RecursiveShapeIterator + */ +class DB_PUBLIC OriginalLayerEdges + : public AsIfFlatEdges +{ +public: + OriginalLayerEdges (); + OriginalLayerEdges (const OriginalLayerEdges &other); + OriginalLayerEdges (const RecursiveShapeIterator &si, bool is_merged = false); + OriginalLayerEdges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); + virtual ~OriginalLayerEdges (); + + EdgesDelegate *clone () const; + + virtual EdgesIteratorDelegate *begin () const; + virtual EdgesIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + + virtual bool is_merged () const; + + virtual const db::Edge *nth (size_t n) const; + virtual bool has_valid_edges () const; + virtual bool has_valid_merged_edges () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Edges &other) const; + virtual bool less (const Edges &other) const; + +private: + OriginalLayerEdges &operator= (const OriginalLayerEdges &other); + + bool m_is_merged; + mutable db::Shapes m_merged_edges; + mutable bool m_merged_edges_valid; + mutable db::RecursiveShapeIterator m_iter; + db::ICplxTrans m_iter_trans; + + void init (); + void ensure_merged_edges_valid () const; +}; + +} + +#endif + diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index e91ad215c..b758f4488 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1659,6 +1659,8 @@ public: } private: + friend class Edges; + RegionDelegate *mp_delegate; void set_delegate (RegionDelegate *delegate); From b9b00a08b541e186cbc0d96bdfb698e5db900f85 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 9 Nov 2018 01:14:22 +0100 Subject: [PATCH 045/335] Many bug fixes after refactoring. --- src/db/db/dbAsIfFlatEdges.cc | 35 ++++++++++++++++++++---------- src/db/db/dbEdges.h | 4 ++-- src/db/db/dbFlatEdges.cc | 2 +- src/db/db/dbOriginalLayerEdges.cc | 15 ++++++++++--- src/db/db/dbOriginalLayerEdges.h | 3 +++ src/db/db/dbOriginalLayerRegion.cc | 7 ++++++ src/db/db/dbOriginalLayerRegion.h | 3 +++ src/db/unit_tests/dbEdges.cc | 6 ++--- 8 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 83ea1d8fb..a97bb9fa9 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -386,9 +386,11 @@ AsIfFlatEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, c db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size ()); + AddressableEdgeDelivery e (begin (), has_valid_edges ()); + size_t n = 0; - for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { - scanner.insert (&*e, n); + for ( ; ! e.at_end (); ++e) { + scanner.insert (e.operator-> (), n); ++n; } @@ -743,16 +745,21 @@ AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Co db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size () + (other ? other->size () : 0)); + AddressableEdgeDelivery e (begin_merged (), has_valid_edges ()); + size_t n = 0; - for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) { - scanner.insert (&*e, n); + for ( ; ! e.at_end (); ++e) { + scanner.insert (e.operator-> (), n); n += 2; } + AddressableEdgeDelivery ee; + if (other) { + ee = other->addressable_merged_edges (); n = 1; - for (EdgesIterator e (other->begin_merged ()); ! e.at_end (); ++e) { - scanner.insert (&*e, n); + for ( ; ! ee.at_end (); ++ee) { + scanner.insert (ee.operator-> (), n); n += 2; } } @@ -779,15 +786,21 @@ AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size () + (other ? other->size () : 0)); - for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + AddressableEdgeDelivery e (begin (), has_valid_edges ()); + + for ( ; ! e.at_end (); ++e) { if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); + scanner.insert (e.operator-> (), 0); } } + + AddressableEdgeDelivery ee; + if (other) { - for (EdgesIterator e (other->begin ()); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 1); + ee = other->addressable_edges (); + for ( ; ! ee.at_end (); ++ee) { + if (! ee->is_degenerate ()) { + scanner.insert (ee.operator-> (), 1); } } } diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index e942a4515..e264b732a 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -873,7 +873,7 @@ public: */ Edges operator- (const Region &other) const { - return Edges (mp_delegate->and_with (other)); + return Edges (mp_delegate->not_with (other)); } /** @@ -883,7 +883,7 @@ public: */ Edges &operator-= (const Region &other) { - set_delegate (mp_delegate->and_with (other)); + set_delegate (mp_delegate->not_with (other)); return *this; } diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 087b73368..2807f1b57 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -106,7 +106,7 @@ FlatEdges::ensure_merged_edges_valid () const m_merged_edges.clear (); - db::Shapes tmp; + db::Shapes tmp (false); EdgeBooleanClusterCollector cluster_collector (&tmp, EdgeOr); db::box_scanner scanner (report_progress (), progress_desc ()); diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc index 47054ecc5..5b33e4f02 100644 --- a/src/db/db/dbOriginalLayerEdges.cc +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -139,6 +139,13 @@ OriginalLayerEdges::clone () const return new OriginalLayerEdges (*this); } +void +OriginalLayerEdges::merged_semantics_changed () +{ + m_merged_edges.clear (); + m_merged_edges_valid = false; +} + EdgesIteratorDelegate * OriginalLayerEdges::begin () const { @@ -245,15 +252,17 @@ OriginalLayerEdges::ensure_merged_edges_valid () const m_merged_edges.clear (); - db::Shapes tmp; + db::Shapes tmp (false); EdgeBooleanClusterCollector cluster_collector (&tmp, EdgeOr); db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size ()); - for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + AddressableEdgeDelivery e (begin (), has_valid_edges ()); + + for ( ; ! e.at_end (); ++e) { if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); + scanner.insert (e.operator-> (), 0); } } diff --git a/src/db/db/dbOriginalLayerEdges.h b/src/db/db/dbOriginalLayerEdges.h index 19f0ab241..13a4d70c5 100644 --- a/src/db/db/dbOriginalLayerEdges.h +++ b/src/db/db/dbOriginalLayerEdges.h @@ -66,6 +66,9 @@ public: virtual bool equals (const Edges &other) const; virtual bool less (const Edges &other) const; +protected: + virtual void merged_semantics_changed (); + private: OriginalLayerEdges &operator= (const OriginalLayerEdges &other); diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc index 7fbc11839..a043ab411 100644 --- a/src/db/db/dbOriginalLayerRegion.cc +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -139,6 +139,13 @@ OriginalLayerRegion::clone () const return new OriginalLayerRegion (*this); } +void +OriginalLayerRegion::merged_semantics_changed () +{ + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + RegionIteratorDelegate * OriginalLayerRegion::begin () const { diff --git a/src/db/db/dbOriginalLayerRegion.h b/src/db/db/dbOriginalLayerRegion.h index 2584dda7b..b712e2843 100644 --- a/src/db/db/dbOriginalLayerRegion.h +++ b/src/db/db/dbOriginalLayerRegion.h @@ -64,6 +64,9 @@ public: virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; +protected: + virtual void merged_semantics_changed (); + private: OriginalLayerRegion &operator= (const OriginalLayerRegion &other); diff --git a/src/db/unit_tests/dbEdges.cc b/src/db/unit_tests/dbEdges.cc index 6a5a3c7b1..d37e10a31 100644 --- a/src/db/unit_tests/dbEdges.cc +++ b/src/db/unit_tests/dbEdges.cc @@ -53,7 +53,7 @@ TEST(1) EXPECT_EQ (r.bbox ().to_string (), "(0,0;100,200)"); EXPECT_EQ (r.transformed (db::Trans (db::Vector (1, 2))).bbox ().to_string (), "(1,2;101,202)"); EXPECT_EQ (r.empty (), false); - EXPECT_EQ (r.is_merged (), false); + EXPECT_EQ (r.is_merged (), true); EXPECT_EQ (r.begin ().at_end (), false); db::Edges r1 = r; @@ -638,7 +638,7 @@ TEST(20) EXPECT_EQ (r1.to_string (), "(120,20;120,40);(120,40;140,40);(140,40;140,20);(140,20;120,20);(160,80;160,140);(220,80;160,80)"); EXPECT_EQ (r1.has_valid_edges (), false); EXPECT_EQ (r1.length (), db::Edges::length_type (200)); - EXPECT_EQ (r1.has_valid_edges (), true); // length, since merging, will implicitly convert to valid edges + EXPECT_EQ (r1.has_valid_edges (), false); EXPECT_EQ (r1.bbox ().to_string (), "(120,20;220,140)"); EXPECT_EQ (r1.size (), size_t (6)); EXPECT_EQ (r1.empty (), false); @@ -649,7 +649,7 @@ TEST(20) EXPECT_EQ (rr.to_string (), "(120,20;120,40);(120,40;140,40);(140,40;140,20);(140,20;120,20)"); db::Edges r2 = r1; - EXPECT_EQ (r2.has_valid_edges (), true); + EXPECT_EQ (r2.has_valid_edges (), false); EXPECT_EQ (r2.length (), db::Edges::length_type (200)); EXPECT_EQ (r2.bbox ().to_string (), "(120,20;220,140)"); EXPECT_EQ (r2.size (), size_t (6)); From 8e19474095ccf17ffb1c45465cdbd0c58bd2e004 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 9 Nov 2018 22:59:26 +0100 Subject: [PATCH 046/335] Introduced edge pairs as valid shapes for db::Shapes --- src/db/db/dbEdgePair.h | 20 +- src/db/db/dbEdgePairs.h | 10 +- src/db/db/dbEdges.cc | 1355 ---------------------------- src/db/db/dbEdges.h | 1232 ------------------------- src/db/db/dbFlatEdges.cc | 2 +- src/db/db/dbObjectWithProperties.h | 4 + src/db/db/dbShape.cc | 11 +- src/db/db/dbShape.h | 121 ++- src/db/db/dbShapeIterator.cc | 5 + src/db/db/dbShapes.cc | 32 + src/db/db/dbShapes.h | 46 +- src/db/db/dbShapes2.cc | 10 + src/db/db/dbShapes3.cc | 16 + src/db/unit_tests/dbShapes.cc | 46 + 14 files changed, 287 insertions(+), 2623 deletions(-) diff --git a/src/db/db/dbEdgePair.h b/src/db/db/dbEdgePair.h index b779a32f1..3ce03c3ef 100644 --- a/src/db/db/dbEdgePair.h +++ b/src/db/db/dbEdgePair.h @@ -53,7 +53,7 @@ public: typedef db::coord_traits coord_traits; typedef typename coord_traits::distance_type distance_type; typedef typename coord_traits::area_type area_type; - typedef db::object_tag< edge > tag; + typedef db::object_tag< edge_pair > tag; /** * @brief The default constructor. @@ -89,6 +89,24 @@ public: // .. nothing else .. } + /** + * @brief The (dummy) translation operator + */ + void translate (const edge_pair &d, db::generic_repository &, db::ArrayRepository &) + { + *this = d; + } + + /** + * @brief The (dummy) translation operator + */ + template + void translate (const edge_pair &d, const T &t, db::generic_repository &, db::ArrayRepository &) + { + *this = d; + transform (t); + } + /** * @brief A less operator to establish a sorting order. */ diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 097617fef..c0f7498f4 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -219,7 +219,7 @@ public: } /** - * @brief Returns the transformed edge set + * @brief Returns the transformed edge pair set */ template EdgePairs transformed (const T &trans) const @@ -230,7 +230,7 @@ public: } /** - * @brief Swap with the other region + * @brief Swaps with the other edge pair set */ void swap (db::EdgePairs &other) { @@ -240,7 +240,7 @@ public: } /** - * @brief Convert to polygons + * @brief Converts to polygons * * Note: because of the include hierarchy we can't use a direct return value. * @@ -263,7 +263,7 @@ public: */ void edges (Edges &output) const; - /* + /** * @brief Returns the first edges * * Note: because of the include hierarchy we can't use a direct return value. @@ -273,7 +273,7 @@ public: */ void first_edges (Edges &output) const; - /* + /** * @brief Returns the second edges * * Note: because of the include hierarchy we can't use a direct return value. diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 421612a05..e4c8abbd8 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -207,1358 +207,3 @@ namespace tl } } -#if 0 -// @@@@@@@@@@@@@@@@@@@@@@@@@@ ORIGINAL @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@qqq - -#include "dbEdges.h" -#include "dbEdgePairs.h" -#include "dbRegion.h" -#include "dbLayoutUtils.h" -#include "dbBoxConvert.h" -#include "dbBoxScanner.h" -#include "dbPolygonTools.h" -#include "dbShapeProcessor.h" - -#include "tlIntervalMap.h" -#include "tlVariant.h" - -#include - -namespace db -{ - -Edges::Edges (const RecursiveShapeIterator &si, bool as_edges) - : m_edges (false), m_merged_edges (false) -{ - init (); - if (! as_edges) { - m_iter = si; - } else { - for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { - insert (s.shape (), s.trans ()); - } - } - m_bbox_valid = false; - m_is_merged = false; -} - -Edges::Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges, bool merged_semantics) - : m_edges (false), m_merged_edges (false) -{ - init (); - if (! as_edges) { - m_iter = si; - m_iter_trans = trans; - } else { - for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { - insert (s.shape (), trans * s.trans ()); - } - } - m_bbox_valid = false; - m_is_merged = false; - m_merged_semantics = merged_semantics; -} - -bool -Edges::operator== (const db::Edges &other) const -{ - if (empty () != other.empty ()) { - return false; - } - if (size () != other.size ()) { - return false; - } - db::Edges::const_iterator o1 = begin (); - db::Edges::const_iterator o2 = other.begin (); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return false; - } - ++o1; - ++o2; - } - return true; -} - -bool -Edges::operator< (const db::Edges &other) const -{ - if (empty () != other.empty ()) { - return empty () < other.empty (); - } - if (size () != other.size ()) { - return (size () < other.size ()); - } - db::Edges::const_iterator o1 = begin (); - db::Edges::const_iterator o2 = other.begin (); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return *o1 < *o2; - } - ++o1; - ++o2; - } - return false; -} - -std::string -Edges::to_string (size_t nmax) const -{ - std::ostringstream os; - const_iterator e = begin (); - bool first = true; - for ( ; ! e.at_end () && nmax != 0; ++e, --nmax) { - if (! first) { - os << ";"; - } - first = false; - os << e->to_string (); - } - if (! e.at_end ()) { - os << "..."; - } - return os.str (); -} - -void -Edges::swap (Edges &other) -{ - std::swap (m_is_merged, other.m_is_merged); - std::swap (m_merged_semantics, other.m_merged_semantics); - m_edges.swap (other.m_edges); - m_merged_edges.swap (other.m_merged_edges); - std::swap (m_bbox, other.m_bbox); - std::swap (m_bbox_valid, other.m_bbox_valid); - std::swap (m_merged_edges_valid, other.m_merged_edges_valid); - std::swap (m_iter, other.m_iter); - std::swap (m_iter_trans, other.m_iter_trans); -} - -void -Edges::invalidate_cache () -{ - m_bbox_valid = false; - m_merged_edges.clear (); - m_merged_edges_valid = false; -} - -void -Edges::disable_progress () -{ - m_report_progress = false; -} - -void -Edges::enable_progress (const std::string &progress_desc) -{ - m_report_progress = true; - m_progress_desc = progress_desc; -} - -Edges::distance_type -Edges::length (const db::Box &box) const -{ - distance_type l = 0; - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - - if (box.empty () || (box.contains (e->p1 ()) && box.contains (e->p2 ()))) { - l += e->length (); - } else { - - std::pair ce = e->clipped (box); - if (ce.first) { - - db::Coord dx = ce.second.dx (); - db::Coord dy = ce.second.dy (); - db::Coord x = ce.second.p1 ().x (); - db::Coord y = ce.second.p1 ().y (); - if ((dx == 0 && x == box.left () && dy < 0) || - (dx == 0 && x == box.right () && dy > 0) || - (dy == 0 && y == box.top () && dx < 0) || - (dy == 0 && y == box.bottom () && dx > 0)) { - // not counted -> box is at outside side of the edge - } else { - l += ce.second.length (); - } - - } - - } - - } - - return l; -} - -Edges -Edges::start_segments (db::Edges::length_type length, double fraction) const -{ - Edges edges; - edges.reserve (m_edges.size ()); - - // zero-length edges would vanish in merged sematics, so we don't set it now - if (length == 0) { - edges.set_merged_semantics (false); - } - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - double l = std::max (e->double_length () * fraction, double (length)); - edges.insert (db::Edge (e->p1 (), db::Point (db::DPoint (e->p1 ()) + db::DVector (e->d()) * (l / e->double_length ())))); - } - - return edges; -} - -Edges -Edges::end_segments (db::Edges::length_type length, double fraction) const -{ - Edges edges; - edges.reserve (m_edges.size ()); - - // zero-length edges would vanish in merged sematics, so we don't set it now - if (length == 0) { - edges.set_merged_semantics (false); - } - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - double l = std::max (e->double_length () * fraction, double (length)); - edges.insert (db::Edge (db::Point (db::DPoint (e->p2 ()) - db::DVector (e->d()) * (l / e->double_length ())), e->p2 ())); - } - - return edges; -} - -Edges -Edges::centers (db::Edges::length_type length, double fraction) const -{ - Edges edges; - edges.reserve (m_edges.size ()); - - // zero-length edges would vanish in merged sematics, so we don't set it now - if (length == 0) { - edges.set_merged_semantics (false); - } - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - double l = std::max (e->double_length () * fraction, double (length)); - db::DVector dl = db::DVector (e->d()) * (0.5 * l / e->double_length ()); - db::DPoint center = db::DPoint (e->p1 ()) + db::DVector (e->p2 () - e->p1 ()) * 0.5; - edges.insert (db::Edge (db::Point (center - dl), db::Point (center + dl))); - } - - return edges; -} - -void -Edges::edge_region_op (const Region &other, bool outside, bool include_borders) -{ - // shortcuts - if (other.empty ()) { - if (! outside) { - clear (); - } - return; - } else if (empty ()) { - return; - } - - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { - if (p->box ().touches (bbox ())) { - ep.insert (*p, 0); - } - } - - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ep.insert (*e, 1); - } - - invalidate_cache (); - - db::EdgeShapeGenerator cc (m_edges, true /*clear*/); - db::EdgePolygonOp op (outside, include_borders); - ep.process (cc, op); - - set_valid_edges (); - - m_is_merged = false; -} - -namespace -{ - -struct OrJoinOp -{ - void operator() (int &v, int n) - { - v += n; - } -}; - -struct AndJoinOp -{ - void operator() (int &v, int n) - { - if (n == 0) { - v = 0; - } - } -}; - -struct NotJoinOp -{ - void operator() (int &v, int n) - { - if (n != 0) { - v = 0; - } - } -}; - -struct XorJoinOp -{ - void operator() (int &v, int n) - { - if (n != 0) { - if (v == 0) { - v = (n > 0 ? 1 : -1); - } else { - v = 0; - } - } - } -}; - -struct EdgeBooleanCluster - : public db::cluster -{ - typedef db::Edge::coord_type coord_type; - - EdgeBooleanCluster (db::Edges *output, db::Edges::BoolOp op) - : mp_output (output), m_op (op) - { - // .. nothing yet .. - } - - void finish () - { - // determine base edge (longest overall edge) - - // shortcut for single edge - if (begin () + 1 == end ()) { - if (begin ()->second == 0) { - if (m_op != db::Edges::And) { - mp_output->insert (*(begin ()->first)); - } - } else { - if (m_op != db::Edges::And && m_op != db::Edges::Not) { - mp_output->insert (*(begin ()->first)); - } - } - return; - } - - db::Edge r = *begin ()->first; - double l1 = 0.0, l2 = r.double_length (); - double n = 1.0 / l2; - db::Point p1 = r.p1 (), p2 = r.p2 (); - - for (iterator o = begin () + 1; o != end (); ++o) { - double ll1 = db::sprod (db::Vector (o->first->p1 () - r.p1 ()), r.d ()) * n; - double ll2 = db::sprod (db::Vector (o->first->p2 () - r.p1 ()), r.d ()) * n; - if (ll1 < l1) { - p1 = o->first->p1 (); - l1 = ll1; - } - if (ll2 < l1) { - p1 = o->first->p2 (); - l1 = ll2; - } - if (ll1 > l2) { - p2 = o->first->p1 (); - l2 = ll1; - } - if (ll2 > l2) { - p2 = o->first->p2 (); - l2 = ll2; - } - } - - db::Vector d = db::Vector (p2 - p1); - n = 1.0 / d.double_length (); - - OrJoinOp or_jop; - AndJoinOp and_jop; - NotJoinOp not_jop; - XorJoinOp xor_jop; - - tl::interval_map a, b; - a.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); - b.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); - - for (iterator o = begin (); o != end (); ++o) { - db::Coord l1 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p1 () - p1), d) * n); - db::Coord l2 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p2 () - p1), d) * n); - if (o->second == 0 || m_op == db::Edges::Or) { - if (l1 < l2) { - a.add (l1, l2, 1, or_jop); - } else if (l1 > l2) { - a.add (l2, l1, -1, or_jop); - } - } else { - if (l1 < l2) { - b.add (l1, l2, 1, or_jop); - } else { - b.add (l2, l1, -1, or_jop); - } - } - } - - tl::interval_map q; - for (tl::interval_map::const_iterator ia = a.begin (); ia != a.end (); ++ia) { - q.add (ia->first.first, ia->first.second, ia->second > 0 ? 1 : (ia->second < 0 ? -1 : 0), or_jop); - } - - if (b.begin () == b.end ()) { - - // optimize for empty b - if (m_op != db::Edges::And) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - if (ib->second > 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.first * n)), p1 + db::Vector (d * (ib->first.second * n)))); - } else if (ib->second < 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.second * n)), p1 + db::Vector (d * (ib->first.first * n)))); - } - } - } - - } else { - - if (m_op == db::Edges::And) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - q.add (ib->first.first, ib->first.second, ib->second, and_jop); - } - } else if (m_op == db::Edges::Not) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - q.add (ib->first.first, ib->first.second, ib->second, not_jop); - } - } else if (m_op == db::Edges::Xor) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - q.add (ib->first.first, ib->first.second, ib->second, xor_jop); - } - } - - for (tl::interval_map::const_iterator iq = q.begin (); iq != q.end (); ++iq) { - if (iq->second > 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.first * n)), p1 + db::Vector (d * (iq->first.second * n)))); - } else if (iq->second < 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.second * n)), p1 + db::Vector (d * (iq->first.first * n)))); - } - } - - } - - } - -private: - db::Edges *mp_output; - db::Edges::BoolOp m_op; -}; - -struct EdgeBooleanClusterCollector - : public db::cluster_collector -{ - EdgeBooleanClusterCollector (db::Edges *output, Edges::BoolOp op) - : db::cluster_collector (EdgeBooleanCluster (output, op), op != Edges::And /*report single*/) - { - // .. nothing yet .. - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - // Select edges which are: - // 1.) not degenerate - // 2.) parallel with some tolerance of roughly 1 dbu - // 3.) connected - if (! o1->is_degenerate () && ! o2->is_degenerate () - && fabs ((double) db::vprod (*o1, *o2)) < db::coord_traits::prec_distance () * std::min (o1->double_length (), o2->double_length ()) - && (o1->p1 () == o2->p1 () || o1->p1 () == o2->p2 () || o1->p2 () == o2->p1 () || o1->p2 () == o2->p2 () || o1->coincident (*o2))) { - db::cluster_collector::add (o1, p1, o2, p2); - } - } -}; - -} - -void -Edges::inplace_boolean (const Edges *other, Edges::BoolOp op) -{ - Edges out = boolean (other, op); - swap (out); -} - -Edges -Edges::boolean (const Edges *other, Edges::BoolOp op) const -{ - Edges output; - EdgeBooleanClusterCollector cluster_collector (&output, op); - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (m_edges.size () + (other ? other->size () : 0)); - - ensure_valid_edges (); - for (const_iterator e = begin (); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); - } - } - if (other) { - other->ensure_valid_edges (); - for (const_iterator e = other->begin (); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 1); - } - } - } - - scanner.process (cluster_collector, 1, db::box_convert ()); - - output.m_is_merged = true; - return output; -} - -void -Edges::ensure_valid_merged_edges () const -{ - // If no merged semantics applies or we will deliver the original - // edges as merged ones, we need to make sure those are valid - // ones (with a unique memory address) - if (! m_merged_semantics || m_is_merged) { - ensure_valid_edges (); - } else { - ensure_merged_edges_valid (); - } -} - -void -Edges::ensure_merged_edges_valid () const -{ - if (! m_merged_edges_valid) { - - m_merged_edges.clear (); - - Edges tmp; - EdgeBooleanClusterCollector cluster_collector (&tmp, Or); - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (m_edges.size ()); - - ensure_valid_edges (); - for (const_iterator e = begin (); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); - } - } - - scanner.process (cluster_collector, 1, db::box_convert ()); - - m_merged_edges.swap (tmp.m_edges); - m_merged_edges_valid = true; - - } -} - -Edges & -Edges::operator+= (const Edges &other) -{ - invalidate_cache (); - - if (! has_valid_edges ()) { - - m_edges.clear (); - - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ++n; - } - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - ++n; - } - - m_edges.reserve (db::Edge::tag (), n); - - for (const_iterator e = begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - - set_valid_edges (); - - } else if (! other.has_valid_edges ()) { - - size_t n = m_edges.size (); - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - ++n; - } - - m_edges.reserve (db::Edge::tag (), n); - - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - - } else { - m_edges.insert (other.m_edges.get_layer ().begin (), other.m_edges.get_layer ().end ()); - } - - m_is_merged = false; - return *this; -} - -namespace -{ - -/** - * @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver - * - * Note: This special scanner uses pointers to two different objects: edges and polygons. - * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate - * pointers to edges. - * - * There is a special box converter which is able to sort that out as well. - */ -template -class edge_to_region_interaction_filter - : public db::box_scanner_receiver -{ -public: - edge_to_region_interaction_filter (OutputContainer &output) - : mp_output (&output) - { - // .. nothing yet .. - } - - void add (const char *o1, size_t p1, const char *o2, size_t p2) - { - const db::Edge *e = 0; - const db::Polygon *p = 0; - - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - if (p1 == 0 && p2 == 1) { - e = reinterpret_cast (o1); - p = reinterpret_cast (o2 - 1); - } else if (p1 == 1 && p2 == 0) { - e = reinterpret_cast (o2); - p = reinterpret_cast (o1 - 1); - } - - if (e && p && m_seen.find (e) == m_seen.end ()) { - if (db::interact (*p, *e)) { - m_seen.insert (e); - mp_output->insert (*e); - } - } - } - -private: - OutputContainer *mp_output; - std::set m_seen; -}; - -/** - * @brief A special box converter that splits the pointers into polygon and edge pointers - */ -struct EdgeOrRegionBoxConverter -{ - typedef db::Box box_type; - - db::Box operator() (const char &c) const - { - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - const char *cp = &c; - if ((size_t (cp) & 1) == 1) { - // it's a polygon - return (reinterpret_cast (cp - 1))->box (); - } else { - // it's an edge - const db::Edge *e = reinterpret_cast (cp); - return db::Box (e->p1 (), e->p2 ()); - } - } -}; - -} - -Edges & -Edges::select_interacting (const Region &other) -{ - // shortcuts - if (other.empty ()) { - clear (); - return *this; - } else if (empty ()) { - return *this; - } - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - AddressablePolygonDelivery p = other.addressable_polygons (); - - for ( ; ! p.at_end (); ++p) { - // NOTE: dirty hack - we can take the address of the polygon because we used ensure_flat_polygons. - scanner.insert ((char *) p.operator-> () + 1, 1); - } - - Edges output; - edge_to_region_interaction_filter filter (output); - EdgeOrRegionBoxConverter bc; - scanner.process (filter, 1, bc); - - swap (output); - return *this; -} - -Edges & -Edges::select_not_interacting (const Region &other) -{ - // shortcuts - if (other.empty () || empty ()) { - return *this; - } - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - AddressablePolygonDelivery p = other.addressable_polygons (); - for ( ; ! p.at_end (); ++p) { - // NOTE: dirty hack - we can take the address of the polygon because we used ensure_flat_polygons. - scanner.insert ((char *) p.operator-> () + 1, 1); - } - - std::set interacting; - edge_to_region_interaction_filter > filter (interacting); - EdgeOrRegionBoxConverter bc; - scanner.process (filter, 1, bc); - - Edges output; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if (interacting.find (*o) == interacting.end ()) { - output.insert (*o); - } - } - - swap (output); - return *this; -} - -namespace -{ - -/** - * @brief A helper class for the edge interaction functionality which acts as an edge pair receiver - */ -template -class edge_interaction_filter - : public db::box_scanner_receiver -{ -public: - edge_interaction_filter (OutputContainer &output) - : mp_output (&output) - { - // .. nothing yet .. - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - // Select the edges which intersect - if (p1 != p2) { - const db::Edge *o = p1 > p2 ? o2 : o1; - const db::Edge *oo = p1 > p2 ? o1 : o2; - if (o->intersect (*oo)) { - if (m_seen.insert (o).second) { - mp_output->insert (*o); - } - } - } - } - -private: - OutputContainer *mp_output; - std::set m_seen; -}; - -} - -Edges & -Edges::select_interacting (const Edges &other) -{ - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, 0); - } - other.ensure_valid_edges (); - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert (&*e, 1); - } - - Edges output; - edge_interaction_filter filter (output); - scanner.process (filter, 1, db::box_convert ()); - - swap (output); - return *this; -} - -Edges & -Edges::select_not_interacting (const Edges &other) -{ - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, 0); - } - other.ensure_valid_edges (); - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert (&*e, 1); - } - - std::set interacting; - edge_interaction_filter > filter (interacting); - scanner.process (filter, 1, db::box_convert ()); - - Edges output; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if (interacting.find (*o) == interacting.end ()) { - output.insert (*o); - } - } - - swap (output); - return *this; -} - -namespace -{ - -struct JoinEdgesCluster - : public db::cluster -{ - typedef db::Edge::coord_type coord_type; - - JoinEdgesCluster (db::Region *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) - : mp_output (output), m_ext_b (ext_b), m_ext_e (ext_e), m_ext_o (ext_o), m_ext_i (ext_i) - { - // .. nothing yet .. - } - - void finish () - { - std::multimap objects_by_p1; - std::multimap objects_by_p2; - for (iterator o = begin (); o != end (); ++o) { - if (o->first->p1 () != o->first->p2 ()) { - objects_by_p1.insert (std::make_pair (o->first->p1 (), o)); - objects_by_p2.insert (std::make_pair (o->first->p2 (), o)); - } - } - - while (! objects_by_p2.empty ()) { - - tl_assert (! objects_by_p1.empty ()); - - // Find the beginning of a new sequence - std::multimap::iterator j0 = objects_by_p1.begin (); - std::multimap::iterator j = j0; - do { - std::multimap::iterator jj = objects_by_p2.find (j->first); - if (jj == objects_by_p2.end ()) { - break; - } else { - j = objects_by_p1.find (jj->second->first->p1 ()); - tl_assert (j != objects_by_p1.end ()); - } - } while (j != j0); - - iterator i = j->second; - - // determine a sequence - // TODO: this chooses any solution in case of forks. Choose a specific one? - std::vector pts; - pts.push_back (i->first->p1 ()); - - do { - - // record the next point - pts.push_back (i->first->p2 ()); - - // remove the edge as it's taken - std::multimap::iterator jj; - for (jj = objects_by_p2.find (i->first->p2 ()); jj != objects_by_p2.end () && jj->first == i->first->p2 (); ++jj) { - if (jj->second == i) { - break; - } - } - tl_assert (jj != objects_by_p2.end () && jj->second == i); - objects_by_p2.erase (jj); - objects_by_p1.erase (j); - - // process along the edge to the next one - // TODO: this chooses any solution in case of forks. Choose a specific one? - j = objects_by_p1.find (i->first->p2 ()); - if (j != objects_by_p1.end ()) { - i = j->second; - } else { - break; - } - - } while (true); - - bool cyclic = (pts.back () == pts.front ()); - - if (! cyclic) { - - // non-cyclic sequence - db::Path path (pts.begin (), pts.end (), 0, m_ext_b, m_ext_e, false); - std::vector hull; - path.hull (hull, m_ext_o, m_ext_i); - db::Polygon poly; - poly.assign_hull (hull.begin (), hull.end ()); - mp_output->insert (poly); - - } else { - - // we have a loop: form a contour by using the polygon size functions and a "Not" to form the hole - db::Polygon poly; - poly.assign_hull (pts.begin (), pts.end ()); - - db::EdgeProcessor ep; - db::RegionPolygonSink ps (*mp_output, false); - db::PolygonGenerator pg (ps, false, true); - - int mode_a = -1, mode_b = -1; - - if (m_ext_o == 0) { - ep.insert (poly, 0); - } else { - db::Polygon sized_poly (poly); - sized_poly.size (m_ext_o, m_ext_o, 2 /*sizing mode*/); - ep.insert (sized_poly, 0); - mode_a = 1; - } - - if (m_ext_i == 0) { - ep.insert (poly, 1); - } else { - db::Polygon sized_poly (poly); - sized_poly.size (-m_ext_i, -m_ext_i, 2 /*sizing mode*/); - ep.insert (sized_poly, 1); - mode_b = 1; - } - - db::BooleanOp2 op (db::BooleanOp::ANotB, mode_a, mode_b); - ep.process (pg, op); - - } - - } - } - -private: - db::Region *mp_output; - coord_type m_ext_b, m_ext_e, m_ext_o, m_ext_i; -}; - -struct JoinEdgesClusterCollector - : public db::cluster_collector -{ - typedef db::Edge::coord_type coord_type; - - JoinEdgesClusterCollector (db::Region *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) - : db::cluster_collector (JoinEdgesCluster (output, ext_b, ext_e, ext_o, ext_i), true) - { - // .. nothing yet .. - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - if (o1->p2 () == o2->p1 () || o1->p1 () == o2->p2 ()) { - db::cluster_collector::add (o1, p1, o2, p2); - } - } -}; - -} - -void -Edges::extended (Region &output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const -{ - if (join) { - - JoinEdgesClusterCollector cluster_collector (&output, ext_b, ext_e, ext_o, ext_i); - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size ()); - - ensure_valid_edges (); - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - scanner.insert (&*e, n); - ++n; - } - - scanner.process (cluster_collector, 1, db::box_convert ()); - - } else { - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - - db::DVector d; - if (e->is_degenerate ()) { - d = db::DVector (1.0, 0.0); - } else { - d = db::DVector (e->d ()) * (1.0 / e->double_length ()); - } - - db::DVector n (-d.y (), d.x ()); - - db::Point pts[4] = { - db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) + n * double (ext_o)), - db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) + n * double (ext_o)), - db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) - n * double (ext_i)), - db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) - n * double (ext_i)), - }; - - db::Polygon poly; - poly.assign_hull (pts + 0, pts + 4); - output.insert (poly); - - } - - } -} - -Edges -Edges::in (const Edges &other, bool invert) const -{ - std::set op; - for (const_iterator o = other.begin_merged (); ! o.at_end (); ++o) { - op.insert (*o); - } - - Edges r; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if ((op.find (*o) == op.end ()) == invert) { - r.insert (*o); - } - } - - return r; -} - -void -Edges::init () -{ - m_report_progress = false; - m_bbox_valid = true; - m_is_merged = true; - m_merged_semantics = true; - m_merged_edges_valid = false; -} - -void -Edges::ensure_bbox_valid () const -{ - if (! m_bbox_valid) { - m_bbox = db::Box (); - for (const_iterator e = begin (); ! e.at_end (); ++e) { - m_bbox += db::Box (e->p1 (), e->p2 ()); - } - m_bbox_valid = true; - } -} - -Edges::const_iterator -Edges::begin_merged () const -{ - if (! m_merged_semantics || m_is_merged) { - return begin (); - } else { - ensure_merged_edges_valid (); - return db::EdgesIterator (m_merged_edges.get_layer ().begin (), m_merged_edges.get_layer ().end ()); - } -} - -std::pair -Edges::begin_iter () const -{ - if (has_valid_edges ()) { - return std::make_pair (db::RecursiveShapeIterator (m_edges), db::ICplxTrans ()); - } else { - return std::make_pair (m_iter, m_iter_trans); - } -} - -std::pair -Edges::begin_merged_iter () const -{ - if (! m_merged_semantics || m_is_merged) { - return begin_iter (); - } else { - ensure_merged_edges_valid (); - return std::make_pair (db::RecursiveShapeIterator (m_merged_edges), db::ICplxTrans ()); - } -} - -void -Edges::insert (const db::Edge &edge) -{ - ensure_valid_edges (); - m_edges.insert (edge); - m_is_merged = false; - invalidate_cache (); -} - -void -Edges::insert (const db::Box &box) -{ - if (! box.empty ()) { - ensure_valid_edges (); - m_edges.insert (db::Edge (box.lower_left (), box.upper_left ())); - m_edges.insert (db::Edge (box.upper_left (), box.upper_right ())); - m_edges.insert (db::Edge (box.upper_right (), box.lower_right ())); - m_edges.insert (db::Edge (box.lower_right (), box.lower_left ())); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Edges::insert (const db::Path &path) -{ - if (path.points () > 0) { - insert (path.polygon ()); - } -} - -void -Edges::insert (const db::Polygon &polygon) -{ - ensure_valid_edges (); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - m_is_merged = false; - invalidate_cache (); -} - -void -Edges::insert (const db::SimplePolygon &polygon) -{ - ensure_valid_edges (); - for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - m_is_merged = false; - invalidate_cache (); -} - -void -Edges::clear () -{ - m_edges.clear (); - m_bbox = db::Box (); - m_bbox_valid = true; - m_is_merged = true; - m_merged_edges.clear (); - m_merged_edges_valid = true; - m_iter = db::RecursiveShapeIterator (); - m_iter_trans = db::ICplxTrans (); -} - -namespace -{ - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - * - * If will perform a edge by edge check using the provided EdgeRelationFilter - */ -class Edge2EdgeCheck - : public db::box_scanner_receiver -{ -public: - Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool requires_different_layers) - : mp_check (&check), mp_output (&output) - { - m_requires_different_layers = requires_different_layers; - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - // Overlap or inside checks require input from different layers - if (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0) { - - // ensure that the first check argument is of layer 1 and the second of - // layer 2 (unless both are of the same layer) - int l1 = int (p1 & size_t (1)); - int l2 = int (p2 & size_t (1)); - - db::EdgePair ep; - if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { - mp_output->insert (ep); - } - - } - } - -private: - const EdgeRelationFilter *mp_check; - EdgePairs *mp_output; - bool m_requires_different_layers; -}; - -} - -EdgePairs -Edges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const -{ - EdgePairs result; - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + (other ? other->size () : 0)); - - ensure_valid_merged_edges (); - size_t n = 0; - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, n); - n += 2; - } - - if (other) { - other->ensure_valid_merged_edges (); - n = 1; - for (const_iterator e = other->begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, n); - n += 2; - } - } - - EdgeRelationFilter check (rel, d, metrics); - check.set_include_zero (other != 0); - check.set_whole_edges (whole_edges); - check.set_ignore_angle (ignore_angle); - check.set_min_projection (min_projection); - check.set_max_projection (max_projection); - - Edge2EdgeCheck edge_check (check, result, other != 0); - scanner.process (edge_check, d, db::box_convert ()); - - return result; -} - -size_t -Edges::size () const -{ - if (! has_valid_edges ()) { - // If we have an iterator, we have to do it the hard way .. - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ++n; - } - return n; - } else { - return m_edges.size (); - } -} - -void -Edges::set_merged_semantics (bool f) -{ - if (f != m_merged_semantics) { - m_merged_semantics = f; - m_merged_edges.clear (); - m_merged_edges_valid = false; - } -} - -void -Edges::set_valid_edges () -{ - m_iter = db::RecursiveShapeIterator (); -} - -void -Edges::ensure_valid_edges () const -{ - if (! has_valid_edges ()) { - - m_edges.clear (); - - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ++n; - } - m_edges.reserve (db::Edge::tag (), n); - - for (const_iterator e = begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - - m_iter = db::RecursiveShapeIterator (); - - } -} - -} // namespace db - -namespace tl -{ - template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::Edges &b) - { - db::Edge e; - - if (! ex.try_read (e)) { - return false; - } - b.insert (e); - - while (ex.test (";")) { - ex.read (e); - b.insert (e); - } - - return true; - } - - template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::Edges &b) - { - if (! test_extractor_impl (ex, b)) { - ex.error (tl::to_string (tr ("Expected an edge collection specification"))); - } - } -} - -#endif - diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index e264b732a..1236b5e05 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -1281,1235 +1281,3 @@ namespace tl } #endif - -#if 0 -// @@@@@@@@@@@@@@@@@@@@@@@@@@ ORIGINAL @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@qqq - -#ifndef HDR_dbEdges -#define HDR_dbEdges - -#include "dbCommon.h" - -#include "dbTypes.h" -#include "dbEdge.h" -#include "dbTrans.h" -#include "dbShape.h" -#include "dbShapes.h" -#include "dbShapes2.h" -#include "dbEdgePairRelations.h" -#include "dbEdgePairs.h" -#include "dbRecursiveShapeIterator.h" -#include "tlString.h" - -namespace db { - -class Edges; - -/** - * @brief A base class for polygon filters - */ -class DB_PUBLIC EdgeFilterBase -{ -public: - EdgeFilterBase () { } - virtual ~EdgeFilterBase () { } - - virtual bool selected (const db::Edge &edge) const = 0; -}; - -/** - * @brief An edge length filter for use with Edges::filter or Edges::filtered - * - * This filter has two parameters: lmin and lmax. - * It will filter all edges for which the length is >= lmin and < lmax. - * There is an "invert" flag which allows to select all edges not - * matching the criterion. - */ - -struct DB_PUBLIC EdgeLengthFilter - : public EdgeFilterBase -{ - typedef db::Edge::distance_type length_type; - - /** - * @brief Constructor - * - * @param lmin The minimum length - * @param lmax The maximum length - * @param inverse If set to true, only polygons not matching this criterion will be filtered - */ - EdgeLengthFilter (length_type lmin, length_type lmax, bool inverse) - : m_lmin (lmin), m_lmax (lmax), m_inverse (inverse) - { - // .. nothing yet .. - } - - /** - * @brief Returns true if the edge length matches the criterion - */ - bool selected (const db::Edge &edge) const - { - length_type l = edge.length (); - if (! m_inverse) { - return l >= m_lmin && l < m_lmax; - } else { - return ! (l >= m_lmin && l < m_lmax); - } - } - -private: - length_type m_lmin, m_lmax; - bool m_inverse; -}; - -/** - * @brief An edge orientation filter for use with Edges::filter or Edges::filtered - * - * This filter has two parameters: amin and amax. - * It will filter all edges for which the orientation angle is >= amin and < amax. - * The orientation angle is measured in degree against the x axis in the mathematical sense. - * There is an "invert" flag which allows to select all edges not - * matching the criterion. - */ - -struct DB_PUBLIC EdgeOrientationFilter - : public EdgeFilterBase -{ - /** - * @brief Constructor - * - * @param amin The minimum angle (measured against the x axis) - * @param amax The maximum angle (measured against the x axis) - * @param inverse If set to true, only polygons not matching this criterion will be filtered - * - * This filter will filter out all edges whose angle against x axis - * is larger or equal to amin and less than amax. - */ - EdgeOrientationFilter (double amin, double amax, bool inverse) - : m_inverse (inverse), m_exact (false) - { - m_emin = db::DVector (cos (amin * M_PI / 180.0), sin (amin * M_PI / 180.0)); - m_emax = db::DVector (cos (amax * M_PI / 180.0), sin (amax * M_PI / 180.0)); - } - - /** - * @brief Constructor - * - * @param a The angle (measured against the x axis) - * @param inverse If set to true, only polygons not matching this criterion will be filtered - * - * This filter will filter out all edges whose angle against x axis - * is equal to a. - */ - EdgeOrientationFilter (double a, bool inverse) - : m_inverse (inverse), m_exact (true) - { - m_emin = db::DVector (cos (a * M_PI / 180.0), sin (a * M_PI / 180.0)); - } - - /** - * @brief Returns true if the edge orientation matches the criterion - */ - bool selected (const db::Edge &edge) const - { - int smin = db::vprod_sign (m_emin, db::DVector (edge.d ())); - if (m_exact) { - if (! m_inverse) { - return smin == 0; - } else { - return smin != 0; - } - } else { - int smax = db::vprod_sign (m_emax, db::DVector (edge.d ())); - if (! m_inverse) { - return (smin >= 0 && smax < 0) || (smax > 0 && smin <= 0); - } else { - return ! ((smin >= 0 && smax < 0) || (smax > 0 && smin <= 0)); - } - } - } - -private: - db::DVector m_emin, m_emax; - bool m_inverse; - bool m_exact; -}; - -/** - * @brief A edge collection iterator - * - * The iterator delivers the edges of the edge collection - */ - -class DB_PUBLIC EdgesIterator -{ -public: - typedef db::Edge value_type; - typedef const db::Edge &reference; - typedef const db::Edge *pointer; - typedef std::forward_iterator_tag iterator_category; - typedef void difference_type; - - /** - * @Returns true, if the iterator is at the end - */ - bool at_end () const - { - return m_from == m_to && m_rec_iter.at_end (); - } - - /** - * @brief Increment - */ - EdgesIterator &operator++ () - { - inc (); - set (); - return *this; - } - - /** - * @brief Access - */ - reference operator* () const - { - if (m_rec_iter.at_end ()) { - return *m_from; - } else { - return m_edge; - } - } - - /** - * @brief Access - */ - pointer operator-> () const - { - if (m_rec_iter.at_end ()) { - return &*m_from; - } else { - return &m_edge; - } - } - -private: - friend class Edges; - - typedef db::layer edge_layer_type; - typedef edge_layer_type::iterator iterator_type; - - db::RecursiveShapeIterator m_rec_iter; - db::ICplxTrans m_iter_trans; - db::Edge m_edge; - iterator_type m_from, m_to; - - /** - * @brief ctor from a recursive shape iterator - */ - EdgesIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans), m_from (), m_to () - { - // NOTE: the following initialization appears to be required on some compilers - // (specifically MacOS/clang) to ensure the proper initialization of the iterators - m_from = m_to; - set (); - } - - /** - * @brief ctor from a range of edges inside a vector - */ - EdgesIterator (iterator_type from, iterator_type to) - : m_from (from), m_to (to) - { - // no required yet: set (); - } - - /** - * @brief Establish the iterator at the current position - */ - void set () - { - while (! m_rec_iter.at_end () && ! m_rec_iter.shape ().is_edge ()) { - inc (); - } - if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().edge (m_edge); - m_edge.transform (m_iter_trans * m_rec_iter.trans ()); - } - } - - /** - * @brief Increment the iterator - */ - void inc () - { - if (! m_rec_iter.at_end ()) { - ++m_rec_iter; - } else { - ++m_from; - } - } -}; - -/** - * @brief An edge set - * - * An edge set is basically a collection of edges. They do not necessarily need to form closed contours. - * Edges can be manipulated in various ways. Edge sets closely cooperate with the Region class which is a - * set of polygons. - * - * Edge sets have some methods in common with regions. Edge sets can also be merged, which means that - * edges which are continuations of other edges are joined. - * - * Edge sets can contain degenerated edges. Such edges are some which have identical start and end points. - * Such edges are basically points which have some applications, i.e. as markers for certain locations. - */ - -class DB_PUBLIC Edges -{ -public: - typedef db::Coord coord_type; - typedef db::coord_traits coord_traits; - typedef db::Edge edge_type; - typedef db::Vector vector_type; - typedef db::Point point_type; - typedef db::Box box_type; - typedef coord_traits::distance_type distance_type; - typedef db::Edge::distance_type length_type; - typedef EdgesIterator const_iterator; - enum BoolOp { Or, Not, Xor, And }; - - /** - * @brief Default constructor - * - * This constructor creates an empty edge set. - */ - Edges () - : m_edges (false), m_merged_edges (false) - { - init (); - } - - /** - * @brief Constructor from an object - * - * Creates a region representing a single instance of that object. - * The object is converted to a polygon and the edges of that polygon are inserted. - */ - template - Edges (const Sh &s) - : m_edges (false), m_merged_edges (false) - { - init (); - insert (s); - } - - /** - * @brief Sequence constructor - * - * Creates a region from a sequence of objects. The objects can be edges, boxes, - * polygons, paths or shapes. This version accepts iterators of the begin ... end - * style. - */ - template - Edges (const Iter &b, const Iter &e) - : m_edges (false), m_merged_edges (false) - { - init (); - reserve (e - b); - for (Iter i = b; i != e; ++i) { - insert (*i); - } - } - - /** - * @brief Constructor from a RecursiveShapeIterator - * - * Creates a region from a recursive shape iterator. This allows to feed an edge set - * from a hierarchy of cells. - * - * If as_edges is false, only edges will be taken from the recursive shape iterator. - * That is somewhat more efficient since it can avoid a copy in some cases. If as_edges - * is false, shapes will be converted to edges. - */ - Edges (const RecursiveShapeIterator &si, bool as_edges = true); - - /** - * @brief Constructor from a RecursiveShapeIterator with a transformation - * - * Creates a region from a recursive shape iterator. This allows to feed an edge set - * from a hierarchy of cells. The transformation is useful to scale to a specific - * DBU for example. - * - * If as_edges is true, only edges will be taken from the recursive shape iterator. - * That is somewhat more efficient since it can avoid a copy in some cases. If as_edges - * is false, shapes will be converted to edges. - */ - Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges = true, bool merged_semantics = true); - - /** - * @brief Enable progress reporting - * - * @param progress_text The description text of the progress object - */ - void enable_progress (const std::string &progress_desc = std::string ()); - - /** - * @brief Disable progress reporting - */ - void disable_progress (); - - /** - * @brief Iterator of the edge set - * - * The iterator delivers the edges of the edge set. - * It follows the at_end semantics. - */ - const_iterator begin () const - { - if (has_valid_edges ()) { - return const_iterator (m_edges.get_layer ().begin (), m_edges.get_layer ().end ()); - } else { - return const_iterator (m_iter, m_iter_trans); - } - } - - /** - * @brief Returns the merged edges if merge semantics applies - * - * If merge semantics is not enabled, this iterator delivers the individual edges. - */ - const_iterator begin_merged () const; - - /** - * @brief Delivers a RecursiveShapeIterator pointing to the edges plus the necessary transformation - */ - std::pair begin_iter () const; - - /** - * @brief Delivers a RecursiveShapeIterator pointing to the merged edges plus the necessary transformation - */ - std::pair begin_merged_iter () const; - - /** - * @brief Insert an edge into the edge set - */ - void insert (const db::Edge &edge); - - /** - * @brief Insert a box into the edge set - * - * This method will insert all edges the box is composed of. - */ - void insert (const db::Box &box); - - /** - * @brief Insert a path into the edge set - * - * This method will insert all edges the path is composed of. - */ - void insert (const db::Path &path); - - /** - * @brief Insert a simple polygon into the edge set - * - * This method will insert all edges the polygon is composed of. - */ - void insert (const db::SimplePolygon &polygon); - - /** - * @brief Insert a polygon into the edge set - * - * This method will insert all edges the polygon is composed of. - */ - void insert (const db::Polygon &polygon); - - /** - * @brief Insert a shape into the region - * - * If the shape is a polygon-type, the shape is converted to a - * polygon and it's edges are inserted into the edge set. - * If the shape is an edge, the edge is inserted into the edge set. - */ - void insert (const db::Shape &shape) - { - if (shape.is_edge ()) { - insert (shape.edge ()); - } else if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - db::Polygon polygon; - shape.polygon (polygon); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - insert (*e); - } - } - } - - /** - * @brief Insert a transformed shape into the edge set - */ - template - void insert (const db::Shape &shape, const T &trans) - { - if (shape.is_edge ()) { - insert (edge_type (trans * shape.edge ())); - } else if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - db::Polygon polygon; - shape.polygon (polygon); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - insert (edge_type (trans * *e)); - } - } - } - - /** - * @brief Returns true if the region is empty - */ - bool empty () const - { - return has_valid_edges () && m_edges.empty (); - } - - /** - * @brief Returns the number of polygons in the region - */ - size_t size () const; - - /** - * @brief Returns a string representing the region - * - * nmax specifies how many polygons are included (set to std::numeric_limits::max() for "all". - */ - std::string to_string (size_t nmax = 10) const; - - /** - * @brief Clear the edge set - */ - void clear (); - - /** - * @brief Reserve memory for the given number of edges - */ - void reserve (size_t n) - { - m_edges.reserve (db::Edge::tag (), n); - } - - /** - * @brief Sets the merged-semantics flag - * - * If merged semantics is enabled (the default), coherent polygons will be considered - * as single regions and artificial edges such as cut-lines will not be considered. - * Merged semantics thus is equivalent to considering coherent areas rather than - * single polygons. - */ - void set_merged_semantics (bool f); - - /** - * @brief Gets the merged-semantics flag - */ - bool merged_semantics () const - { - return m_merged_semantics; - } - - /** - * @brief Returns true if the region is merged - */ - bool is_merged () const - { - return m_is_merged; - } - - /** - * @brief Returns the total length of the edges - * Merged semantics applies. In merged semantics, the length is the correct total length of the edges. - * Without merged semantics, overlapping parts are counted twice. - * - * If a box is given, the computation is restricted to that box. - * Edges coincident with the box edges are counted only if the form outer edges at the box edge. - */ - length_type length (const db::Box &box = db::Box ()) const; - - /** - * @brief Returns the bounding box of the region - */ - Box bbox () const - { - ensure_bbox_valid (); - return m_bbox; - } - - /** - * @brief Filters the edge set - * - * This method will keep all edges for which the filter returns true. - * Merged semantics applies. - */ - Edges &filter (EdgeFilterBase &filter) - { - edge_iterator_type ew = m_edges.get_layer ().begin (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - if (filter.selected (*e)) { - if (ew == m_edges.get_layer ().end ()) { - m_edges.get_layer ().insert (*e); - ew = m_edges.get_layer ().end (); - } else { - m_edges.get_layer ().replace (ew++, *e); - } - } - } - m_edges.get_layer ().erase (ew, m_edges.get_layer ().end ()); - m_merged_edges.clear (); - m_is_merged = m_merged_semantics; - m_iter = db::RecursiveShapeIterator (); - return *this; - } - - /** - * @brief Returns the filtered edges - * - * This method will return a new region with only those edges which - * conform to the filter criterion. - * Merged semantics applies. - */ - Edges filtered (const EdgeFilterBase &filter) const - { - Edges d; - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - if (filter.selected (*e)) { - d.insert (*e); - } - } - return d; - } - - /** - * @brief Returns all edges found in the other edge collection as well - * - * If "invert" is true, all edges not found in the other collection - * are returned. - */ - Edges in (const Edges &other, bool invert) const; - - /** - * @brief Transform the edge set - */ - template - Edges &transform (const T &trans) - { - if (! trans.is_unity ()) { - ensure_valid_edges (); - for (edge_iterator_type e = m_edges.get_layer ().begin (); e != m_edges.get_layer ().end (); ++e) { - m_edges.get_layer ().replace (e, e->transformed (trans)); - } - m_iter_trans = db::ICplxTrans (trans) * m_iter_trans; - m_bbox_valid = false; - } - return *this; - } - - /** - * @brief Returns the transformed edge set - */ - template - Edges transformed (const T &trans) const - { - Edges d (*this); - d.transform (trans); - return d; - } - - /** - * @brief Swap with the other region - */ - void swap (db::Edges &other); - - /** - * @brief returns the extended edges - * - * Edges are extended by creating a rectangle on each edge. The rectangle is constructed on the - * edge by applying the extensions given by ext_o, ext_b, ext_e, ext_i at the outside, the - * beginning, the end and the inside. - * If the edge is laid flat pointing from left to right, the outside is at the top, the inside - * is at the bottom. - * - * For degenerated edges with length 0, the orientation is assumed to the horizontal. The extended - * method creates a rectangle with specified dimensions: ext_b to the left, ext_o to the top, ext_i to the bottom - * and ext_e to the right. - * - * If the joined parameter is set to true, adjacent edges are joined before the extension is applied. - * A the join points, the extension is created similar to what the sizing function does. - * - * Note: the output is given as an out parameter since because of the include hierarchy we can't use - * Region as a return value directly. - * - * Merged semantics applies. - */ - void extended (Region &output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join = false) const; - - /** - * @brief Returns edges (point-like) representing the start part of the edges - * - * The length of the part can be choosen by length or a fraction of the original length. - * If length and fraction are 0, a point at the beginning of the edge will be created. - * If length is non-zero and fraction is 0, a segment in the direction of the edge - * with the given length is created, even if the length is larger than the original - * edge. - * - * If fraction is given and length is 0, the segment will have a length which is the specified - * fraction of the original edge. - * If both values are given, the resulting edge will have a length which is the maximum of - * both the fixed length and the length derived from the fraction. - * - * Length and fraction can be negative in which case the resulting segment will point - * in the opposite direction of the original edge. - * - * Merged semantics applies. - */ - Edges start_segments (length_type length, double fraction) const; - - /** - * @brief Returns edges (point-like) representing the end part of the edges - * - * This method behaves similar to \start_segments but creates segments at the end of - * the edges. - * - * Merged semantics applies. - */ - Edges end_segments (length_type length, double fraction) const; - - /** - * @brief Returns edges (point-like) representing the center of the edges - * - * This method behaves similar to \start_segments but creates segments at the centers of - * the edges. - * - * Merged semantics applies. - */ - Edges centers (length_type length, double fraction) const; - - /** - * @brief Boolean AND operator - * - * This operation returns the parts of the edges which coincide with edges from "other" - * After this operation the edges are not necessarily merged. - */ - Edges operator& (const Edges &other) const - { - return boolean (&other, And); - } - - /** - * @brief In-place boolean AND operator - */ - Edges &operator&= (const Edges &other) - { - inplace_boolean (&other, And); - return *this; - } - - /** - * @brief Boolean AND operator with a region - * - * This operation returns the parts of the edges which are inside the given region. - * Edges on the borders of the polygons are included in the edge set. - * As a side effect, the edges are made non-intersecting by introducing cut points where - * edges intersect. - */ - Edges operator& (const Region &other) const - { - Edges d (*this); - d &= other; - return d; - } - - /** - * @brief In-place boolean AND operator with a region - */ - Edges &operator&= (const Region &other) - { - edge_region_op (other, false /*inside*/, true /*include borders*/); - return *this; - } - - /** - * @brief Boolean NOT operator - * - * This operation returns the parts of the edges which do not coincide with edges from "other" - * After this operation the edges are not necessarily merged. - */ - Edges operator- (const Edges &other) const - { - return boolean (&other, Not); - } - - /** - * @brief In-place boolean NOT operator - */ - Edges &operator-= (const Edges &other) - { - inplace_boolean (&other, Not); - return *this; - } - - /** - * @brief Boolean NOT operator with a region - * - * This operation returns the parts of the edges which are outside the given region. - * Edges on the borders of the polygons are removed from the edge set. - * As a side effect, the edges are made non-intersecting by introducing cut points where - * edges intersect. - */ - Edges operator- (const Region &other) const - { - Edges d (*this); - d -= other; - return d; - } - - /** - * @brief In-place boolean NOT operator with a region - */ - Edges &operator-= (const Region &other) - { - edge_region_op (other, true /*outside*/, true /*include borders*/); - return *this; - } - - /** - * @brief Boolean XOR operator - * - * This operation returns the parts of the edges which do not coincide with edges from "other" - * and vice versa. - * After this operation the edges are not necessarily merged. - */ - Edges operator^ (const Edges &other) const - { - return boolean (&other, Xor); - } - - /** - * @brief In-place boolean XOR operator - */ - Edges &operator^= (const Edges &other) - { - inplace_boolean (&other, Xor); - return *this; - } - - /** - * @brief Joining of edge sets - * - * This method will combine the edges from "other" with the egdes of "this". - * After this operation the edges are not necessarily merged. - */ - Edges operator+ (const Edges &other) const - { - Edges d (*this); - d += other; - return d; - } - - /** - * @brief In-place joining of edge sets - */ - Edges &operator+= (const Edges &other); - - /** - * @brief Boolean OR operator - * - * This method will combine the edges from "other" with the egdes of "this". - * After this operation the edges are usually merged. - */ - Edges operator| (const Edges &other) const - { - return boolean (&other, Or); - } - - /** - * @brief In-place boolean OR operator - */ - Edges &operator|= (const Edges &other) - { - inplace_boolean (&other, Or); - return *this; - } - - /** - * @brief Select the edges inside the given region - * - * This method will select the edges inside the given region. - * Edges on the border of the region won't be selected. - * As a side effect, the edges are made non-intersecting by introducing cut points where - * edges intersect. - */ - Edges &select_inside_part (const Region &other) - { - edge_region_op (other, false /*inside*/, false /*don't include borders*/); - return *this; - } - - /** - * @brief Returns the edges inside the given region - * - * This is an out-of-place version of "select_inside_part". - */ - Edges inside_part (const Region &other) const - { - Edges d (*this); - d.select_inside_part (other); - return d; - } - - /** - * @brief Select the edge parts outside of the given region - * - * This method will select the edge parts outside of the given region. - * Edges on the border of the region won't be selected. - * As a side effect, the edges are made non-intersecting by introducing cut points where - * edges intersect. - */ - Edges &select_outside_part (const Region &other) - { - edge_region_op (other, true /*outside*/, false /*don't include borders*/); - return *this; - } - - /** - * @brief Returns the edge parts outside of the given region - * - * This is an out-of-place version of "select_outside_part". - */ - Edges outside_part (const Region &other) const - { - Edges d (*this); - d.select_outside_part (other); - return d; - } - - /** - * @brief Selects all edges of this edge set which overlap or touch with polygons from the region - * - * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be - * selected as a whole. - */ - Edges &select_interacting (const Region &other); - - /** - * @brief Returns all edges of this edge set which overlap or touch with polygons from the region - * - * This method is an out-of-place version of select_interacting. - */ - Edges selected_interacting (const Region &other) const - { - Edges d (*this); - d.select_interacting (other); - return d; - } - - /** - * @brief Selects all edges of this edge set which do not overlap or touch with polygons from the region - * - * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be - * selected as a whole. - */ - Edges &select_not_interacting (const Region &other); - - /** - * @brief Returns all edges of this edge set which do not overlap or touch with polygons from the region - * - * This method is an out-of-place version of select_not_interacting. - */ - Edges selected_not_interacting (const Region &other) const - { - Edges d (*this); - d.select_not_interacting (other); - return d; - } - - /** - * @brief Selects all edges of this edge set which overlap or touch with edges from the other edge set - * - * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be - * selected as a whole. - */ - Edges &select_interacting (const Edges &other); - - /** - * @brief Returns all edges of this edge set which overlap or touch with edges from the other edge set - * - * This method is an out-of-place version of select_interacting. - */ - Edges selected_interacting (const Edges &other) const - { - Edges d (*this); - d.select_interacting (other); - return d; - } - - /** - * @brief Selects all edges of this edge set which do not overlap or touch with edges from the other edge set - * - * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be - * selected as a whole. - */ - Edges &select_not_interacting (const Edges &other); - - /** - * @brief Returns all edges of this edge set which do not overlap or touch with edges from the other edge set - * - * This method is an out-of-place version of select_not_interacting. - */ - Edges selected_not_interacting (const Edges &other) const - { - Edges d (*this); - d.select_not_interacting (other); - return d; - } - - /** - * @brief Merge the edge set - * - * This method merges the edges of the edge set if they are not merged already. - * It returns a reference to this edge set. - * Edges are merged by joining them if one edge is a continuation of another. - * An out-of-place merge version is "merged". - */ - Edges &merge () - { - if (! is_merged ()) { - inplace_boolean (0, Or); - } - return *this; - } - - /* - * @brief Returns the merged edge set - * - * This is the out-of-place merge. It returns a new edge set but does not modify - * the edge set it is called on. An in-place version is "merge". - */ - Edges merged () const - { - return boolean (0, Or); - } - - /** - * @brief Applies a width check and returns EdgePairs which correspond to violation markers - * - * The width check will create a edge pairs if the width of the area between the - * edges is less than the specified threshold d. Without "whole_edges", the parts of - * the edges are returned which violate the condition. If "whole_edges" is true, the - * result will contain the complete edges participating in the result. - * - * "Width" refers to the space between the "inside" sides of the edges. - * - * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" - * metrics are available. - * - * ignore_angle allows specification of a maximum angle the edges can have to not participate - * in the check. By choosing 90 degree, edges having an angle of 90 degree and larger are not checked, - * but acute corners are for example. - * - * With min_projection and max_projection it is possible to specify how edges must be related - * to each other. If the length of the projection of either edge on the other is >= min_projection - * or < max_projection, the edges are considered for the check. - * - * The order of the edges in the resulting edge pairs is undefined. - * - * Merged semantics applies. - */ - EdgePairs width_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::WidthRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a space check and returns EdgePairs which correspond to violation markers - * - * "Space" refers to the space between the "outside" sides of the edges. - * - * For the parameters see \width_check. The space check reports edges for which the space is - * less than the specified threshold d. - * - * Merged semantics applies. - */ - EdgePairs space_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::SpaceRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies an enclosing check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "inside" side of the edge from this edge set, the orientation is parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs enclosing_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::OverlapRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies an overlap check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "inside" side of the edge from this edge set, the orientation is anti-parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs overlap_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::WidthRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies an separation check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "outside" side of the edge from this edge set, the orientation is anti-parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs separation_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::SpaceRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a inside check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "outide" side of the edge from this edge set, the orientation is parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs inside_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::InsideRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Returns the nth edge - * - * This method will force the edges to be inside the edge vector and will invalidate any iterator. - * If that happens, the method may be costly. - * The iterator should be used whenever possible. - */ - const db::Edge *nth (size_t n) const - { - ensure_valid_edges (); - return n < m_edges.size () ? &m_edges.get_layer ().begin () [n] : 0; - } - - /** - * @brief Returns true, if the edge set has valid edges stored within itself - */ - bool has_valid_edges () const - { - return m_iter.at_end (); - } - - /** - * @brief Ensures the edge collection has valid edges - * - * This method is const since it has const semantics. - */ - void ensure_valid_edges () const; - - /** - * @brief Ensures the edge collection has valid merged edges - * - * It will make sure that begin_merged will deliver an - * iterator to an edge with a unique memory location. - */ - void ensure_valid_merged_edges () const; - - /** - * @brief Equality - */ - bool operator== (const db::Edges &other) const; - - /** - * @brief Inequality - */ - bool operator!= (const db::Edges &other) const - { - return !operator== (other); - } - - /** - * @brief Less operator - */ - bool operator< (const db::Edges &other) const; - -private: - typedef db::layer edge_layer_type; - typedef edge_layer_type::iterator edge_iterator_type; - - bool m_is_merged; - bool m_merged_semantics; - mutable db::Shapes m_edges; - mutable db::Shapes m_merged_edges; - mutable db::Box m_bbox; - mutable bool m_bbox_valid; - mutable bool m_merged_edges_valid; - mutable db::RecursiveShapeIterator m_iter; - db::ICplxTrans m_iter_trans; - bool m_report_progress; - std::string m_progress_desc; - - void init (); - void invalidate_cache (); - void set_valid_edges (); - void ensure_bbox_valid () const; - void ensure_merged_edges_valid () const; - EdgePairs run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - void inplace_boolean (const Edges *other, BoolOp op); - Edges boolean (const Edges *other, BoolOp op) const; - void edge_region_op (const Region &other, bool outside, bool include_borders); -}; - -} // namespace db - -namespace tl -{ - /** - * @brief The type traits for the edges type - */ - template <> - struct type_traits : public type_traits - { - typedef true_tag supports_extractor; - typedef true_tag supports_to_string; - typedef true_tag has_less_operator; - typedef true_tag has_equal_operator; - }; - -} - -#endif - -#endif diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 2807f1b57..4c33b6575 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -96,7 +96,7 @@ void FlatEdges::merged_semantics_changed () void FlatEdges::reserve (size_t n) { - m_edges.reserve (db::Polygon::tag (), n); + m_edges.reserve (db::Edge::tag (), n); } void diff --git a/src/db/db/dbObjectWithProperties.h b/src/db/db/dbObjectWithProperties.h index 0a52dfef0..d8afb6818 100644 --- a/src/db/db/dbObjectWithProperties.h +++ b/src/db/db/dbObjectWithProperties.h @@ -29,6 +29,7 @@ #include "dbPolygon.h" #include "dbPath.h" #include "dbEdge.h" +#include "dbEdgePair.h" #include "dbText.h" #include "dbBox.h" #include "dbArray.h" @@ -196,6 +197,9 @@ typedef object_with_properties DPathRefWithProperties; typedef object_with_properties EdgeWithProperties; typedef object_with_properties DEdgeWithProperties; +typedef object_with_properties EdgePairWithProperties; +typedef object_with_properties DEdgePairWithProperties; + typedef object_with_properties TextWithProperties; typedef object_with_properties DTextWithProperties; typedef object_with_properties TextRefWithProperties; diff --git a/src/db/db/dbShape.cc b/src/db/db/dbShape.cc index baaafbdcb..f09365a80 100644 --- a/src/db/db/dbShape.cc +++ b/src/db/db/dbShape.cc @@ -52,6 +52,8 @@ db::properties_id_type Shape::prop_id () const return (**((psimple_polygon_ptr_array_iter_type *) m_generic.iter)).properties_id (); case Edge: return (**((pedge_iter_type *) m_generic.iter)).properties_id (); + case EdgePair: + return (**((pedge_pair_iter_type *) m_generic.iter)).properties_id (); case Path: return (**((ppath_iter_type *) m_generic.iter)).properties_id (); case PathRef: @@ -99,6 +101,8 @@ db::properties_id_type Shape::prop_id () const return m_generic.psimple_polygon_aref->properties_id (); case Edge: return m_generic.pedge->properties_id (); + case EdgePair: + return m_generic.pedge_pair->properties_id (); case Path: return m_generic.ppath->properties_id (); case PathRef: @@ -701,6 +705,8 @@ Shape::box_type Shape::bbox () const return basic_ptr (text_ptr_array_type::tag ())->bbox (db::box_convert ()); case Edge: return box_type (edge ().p1 (), edge ().p2 ()); + case EdgePair: + return edge_pair ().bbox (); case Path: return path ().box (); case PathRef: @@ -772,6 +778,9 @@ Shape::to_string () const case Edge: r = "edge " + edge ().to_string (); break; + case EdgePair: + r = "edge_pair " + edge_pair ().to_string (); + break; case Path: case PathRef: case PathPtrArrayMember: @@ -810,6 +819,4 @@ Shape::to_string () const return r; } - } - diff --git a/src/db/db/dbShape.h b/src/db/db/dbShape.h index 63446c641..76220df8b 100644 --- a/src/db/db/dbShape.h +++ b/src/db/db/dbShape.h @@ -32,6 +32,7 @@ #include "dbPolygon.h" #include "dbPath.h" #include "dbEdge.h" +#include "dbEdgePair.h" #include "dbText.h" #include "dbBox.h" #include "dbBoxConvert.h" @@ -616,6 +617,7 @@ public: typedef db::path_ref path_ptr_type; typedef db::array path_ptr_array_type; typedef db::edge edge_type; + typedef db::edge_pair edge_pair_type; typedef db::text text_type; typedef db::text_ref text_ref_type; typedef db::text_ref text_ptr_type; @@ -642,6 +644,7 @@ public: typedef tl::reuse_vector >::const_iterator path_ptr_iter_type; typedef tl::reuse_vector >::const_iterator path_ptr_array_iter_type; typedef tl::reuse_vector >::const_iterator edge_iter_type; + typedef tl::reuse_vector >::const_iterator edge_pair_iter_type; typedef tl::reuse_vector >::const_iterator text_iter_type; typedef tl::reuse_vector >::const_iterator text_ref_iter_type; typedef tl::reuse_vector >::const_iterator text_ptr_iter_type; @@ -665,6 +668,7 @@ public: typedef tl::reuse_vector > >::const_iterator ppath_ptr_iter_type; typedef tl::reuse_vector > >::const_iterator ppath_ptr_array_iter_type; typedef tl::reuse_vector > >::const_iterator pedge_iter_type; + typedef tl::reuse_vector > >::const_iterator pedge_pair_iter_type; typedef tl::reuse_vector > >::const_iterator ptext_iter_type; typedef tl::reuse_vector > >::const_iterator ptext_ref_iter_type; typedef tl::reuse_vector > >::const_iterator ptext_ptr_iter_type; @@ -690,6 +694,7 @@ public: SimplePolygonPtrArray, SimplePolygonPtrArrayMember, Edge, + EdgePair, Path, PathRef, PathPtrArray, @@ -1016,7 +1021,15 @@ public: m_type = Edge; } - /** + /** + * @brief Construct a shape proxy as a reference to a edge pair + */ + void init (edge_pair_type::tag) + { + m_type = EdgePair; + } + + /** * @brief Construct a shape proxy as a reference to a box */ void init (box_type::tag) @@ -1344,7 +1357,23 @@ public: } } - /** + /** + * @brief Return the actual object that this shape reference is pointing to + * + * This is a generalisation of the polygon (), etc. methods using a tag to identify the + * target object. + */ + const edge_pair_type *basic_ptr (edge_pair_type::tag) const + { + tl_assert (m_type == EdgePair); + if (m_stable) { + return m_with_props ? &**(((pedge_pair_iter_type *) m_generic.iter)) : &**(((edge_pair_iter_type *) m_generic.iter)); + } else { + return m_with_props ? m_generic.pedge_pair : m_generic.edge_pair; + } + } + + /** * @brief Return the actual object that this shape reference is pointing to * * This is a generalisation of the polygon (), etc. methods using a tag to identify the @@ -1636,7 +1665,24 @@ public: } } - /** + /** + * @brief Return the actual object that this shape reference is pointing to for objects with properties + * + * This is a generalisation of the polygon (), etc. methods using a tag to identify the + * target object. + */ + const db::object_with_properties *basic_ptr (db::object_with_properties::tag) const + { + tl_assert (m_type == EdgePair); + tl_assert (m_with_props); + if (m_stable) { + return &**(((pedge_pair_iter_type *) m_generic.iter)); + } else { + return m_generic.pedge_pair; + } + } + + /** * @brief Return the actual object that this shape reference is pointing to for objects with properties * * This is a generalisation of the polygon (), etc. methods using a tag to identify the @@ -1862,7 +1908,16 @@ public: return *(((edge_iter_type *) m_generic.iter)); } - /** + /** + * @brief Return the iterator (in stable reference mode) by tag + */ + edge_pair_iter_type basic_iter (edge_pair_type::tag) const + { + tl_assert (m_type == EdgePair && ! m_with_props); + return *(((edge_pair_iter_type *) m_generic.iter)); + } + + /** * @brief Return the iterator (in stable reference mode) by tag */ text_iter_type basic_iter (text_type::tag) const @@ -2024,7 +2079,16 @@ public: return *(((pedge_iter_type *) m_generic.iter)); } - /** + /** + * @brief Return the iterator (in stable reference mode) by tag for objects with properties + */ + pedge_pair_iter_type basic_iter (db::object_with_properties::tag) const + { + tl_assert (m_type == EdgePair && m_with_props); + return *(((pedge_pair_iter_type *) m_generic.iter)); + } + + /** * @brief Return the iterator (in stable reference mode) by tag for objects with properties */ ptext_iter_type basic_iter (db::object_with_properties::tag) const @@ -2288,7 +2352,50 @@ public: return edge (p); } - /** + /** + * @brief Return a reference to the edge pair if one is referenced + */ + const edge_pair_type &edge_pair () const + { + tl_assert (m_type == EdgePair); + return *basic_ptr (edge_pair_type::tag ()); + } + + /** + * @brief Test if the shape proxy points to a edge pair + */ + bool is_edge_pair () const + { + return (m_type == EdgePair); + } + + /** + * @brief Instantiate the edge pair object + * + * If an edge pair is referenced, this object is instantiated + * by this method. + * Returns true, if the conversion was successful. + */ + bool edge_pair (edge_pair_type &e) const + { + if (is_edge_pair ()) { + e = edge_pair (); + return true; + } else { + return false; + } + } + + /** + * @brief Alias for polymorphic expansion + * Returns true, if the conversion was successful. + */ + bool instantiate (edge_pair_type &p) const + { + return edge_pair (p); + } + + /** * @brief Return a reference to the text if one is referenced */ const text_type &text () const @@ -2567,6 +2674,7 @@ public: const text_ref_type *text_ref; const text_ptr_array_type *text_aref; const edge_type *edge; + const edge_pair_type *edge_pair; const path_type *path; const path_ref_type *path_ref; const path_ptr_array_type *path_aref; @@ -2586,6 +2694,7 @@ public: const db::object_with_properties *ptext_ref; const db::object_with_properties *ptext_aref; const db::object_with_properties *pedge; + const db::object_with_properties *pedge_pair; const db::object_with_properties *ppath; const db::object_with_properties *ppath_ref; const db::object_with_properties *ppath_aref; diff --git a/src/db/db/dbShapeIterator.cc b/src/db/db/dbShapeIterator.cc index f976b2a15..4091dbf5c 100644 --- a/src/db/db/dbShapeIterator.cc +++ b/src/db/db/dbShapeIterator.cc @@ -634,6 +634,9 @@ ShapeIterator::advance_generic (int mode) case Edge: if (advance_shape (mode)) return; break; + case EdgePair: + if (advance_shape (mode)) return; + break; case Path: if (advance_shape (mode)) return; break; @@ -758,6 +761,8 @@ ShapeIterator::quad_box_generic () const return (quad_box_by_shape (region_tag)); case Edge: return (quad_box_by_shape (region_tag)); + case EdgePair: + return (quad_box_by_shape (region_tag)); case Path: return (quad_box_by_shape (region_tag)); case PathRef: diff --git a/src/db/db/dbShapes.cc b/src/db/db/dbShapes.cc index 1b4bcd13d..37f21b25e 100644 --- a/src/db/db/dbShapes.cc +++ b/src/db/db/dbShapes.cc @@ -295,6 +295,8 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Shapes::unit_trans_typ return (insert_array_by_tag (shape_type::simple_polygon_ptr_array_type::tag (), shape, shape_repository (), pm)); case shape_type::Edge: return (insert_by_tag (shape_type::edge_type::tag (), shape, pm)); + case shape_type::EdgePair: + return (insert_by_tag (shape_type::edge_pair_type::tag (), shape, pm)); case shape_type::Path: return (insert_by_tag (shape_type::path_type::tag (), shape, pm)); case shape_type::PathRef: @@ -439,6 +441,16 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_del return insert (db::object_with_properties (p, pm (shape.prop_id ()))); } } + case shape_type::EdgePair: + { + shape_type::edge_pair_type p (shape.edge_pair ()); + p.transform (t); + if (! shape.has_prop_id ()) { + return insert (p); + } else { + return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + } + } case shape_type::Path: { shape_type::path_type p (shape.path ()); @@ -554,6 +566,8 @@ Shapes::find (const Shapes::shape_type &shape) const return find_shape_by_tag (shape_type::simple_polygon_ptr_array_type::tag (), shape); case shape_type::Edge: return find_shape_by_tag (shape_type::edge_type::tag (), shape); + case shape_type::EdgePair: + return find_shape_by_tag (shape_type::edge_pair_type::tag (), shape); case shape_type::Path: return find_shape_by_tag (shape_type::path_type::tag (), shape); case shape_type::PathRef: @@ -620,6 +634,9 @@ Shapes::replace_prop_id (const Shapes::shape_type &ref, db::properties_id_type p case shape_type::Edge: replace_prop_id (ref.basic_ptr (object_with_properties::tag ()), prop_id); break; + case shape_type::EdgePair: + replace_prop_id (ref.basic_ptr (object_with_properties::tag ()), prop_id); + break; case shape_type::Path: replace_prop_id (ref.basic_ptr (object_with_properties::tag ()), prop_id); break; @@ -682,6 +699,8 @@ Shapes::replace_prop_id (const Shapes::shape_type &ref, db::properties_id_type p return replace_prop_id_iter (shape_type::simple_polygon_ptr_array_type::tag (), ref.basic_iter (shape_type::simple_polygon_ptr_array_type::tag ()), prop_id); case shape_type::Edge: return replace_prop_id_iter (shape_type::edge_type::tag (), ref.basic_iter (shape_type::edge_type::tag ()), prop_id); + case shape_type::EdgePair: + return replace_prop_id_iter (shape_type::edge_pair_type::tag (), ref.basic_iter (shape_type::edge_pair_type::tag ()), prop_id); case shape_type::Path: return replace_prop_id_iter (shape_type::path_type::tag (), ref.basic_iter (shape_type::path_type::tag ()), prop_id); case shape_type::PathRef: @@ -759,6 +778,12 @@ Shapes::transform (const Shapes::shape_type &ref, const Trans &t) p.transform (t); return replace_member_with_props (shape_type::edge_type::tag (), ref, p); } + case shape_type::EdgePair: + { + shape_type::edge_pair_type p (ref.edge_pair ()); + p.transform (t); + return replace_member_with_props (shape_type::edge_pair_type::tag (), ref, p); + } case shape_type::Path: { shape_type::path_type p (ref.path ()); @@ -845,6 +870,8 @@ Shapes::replace (const Shapes::shape_type &ref, const Sh &sh) return replace_member_with_props (shape_type::simple_polygon_ptr_array_type::tag (), ref, sh); case shape_type::Edge: return replace_member_with_props (shape_type::edge_type::tag (), ref, sh); + case shape_type::EdgePair: + return replace_member_with_props (shape_type::edge_pair_type::tag (), ref, sh); case shape_type::Path: return replace_member_with_props (shape_type::path_type::tag (), ref, sh); case shape_type::PathRef: @@ -1212,6 +1239,7 @@ template DB_PUBLIC Shape Shapes::replace<>(const Shape &, const Polygon &); template DB_PUBLIC Shape Shapes::replace<>(const Shape &, const SimplePolygon &); template DB_PUBLIC Shape Shapes::replace<>(const Shape &, const Text &); template DB_PUBLIC Shape Shapes::replace<>(const Shape &, const Edge &); +template DB_PUBLIC Shape Shapes::replace<>(const Shape &, const EdgePair &); template DB_PUBLIC Shape Shapes::transform<> (const Shape &, const ICplxTrans &); template DB_PUBLIC Shape Shapes::transform<> (const Shape &, const Trans &); @@ -1239,6 +1267,8 @@ template class DB_PUBLIC layer_op, db::stable_layer_tag>; template class DB_PUBLIC layer_op; template class DB_PUBLIC layer_op, db::stable_layer_tag>; +template class DB_PUBLIC layer_op; +template class DB_PUBLIC layer_op, db::stable_layer_tag>; template class DB_PUBLIC layer_op; template class DB_PUBLIC layer_op, db::stable_layer_tag>; template class DB_PUBLIC layer_op; @@ -1275,6 +1305,8 @@ template class DB_PUBLIC layer_op, db::unstable_layer_tag>; template class DB_PUBLIC layer_op; template class DB_PUBLIC layer_op, db::unstable_layer_tag>; +template class DB_PUBLIC layer_op; +template class DB_PUBLIC layer_op, db::unstable_layer_tag>; template class DB_PUBLIC layer_op; template class DB_PUBLIC layer_op, db::unstable_layer_tag>; template class DB_PUBLIC layer_op; diff --git a/src/db/db/dbShapes.h b/src/db/db/dbShapes.h index 6fa907b9b..94d87b7bb 100644 --- a/src/db/db/dbShapes.h +++ b/src/db/db/dbShapes.h @@ -83,6 +83,7 @@ public: typedef db::array path_ptr_array_type; typedef path_ptr_array_type::iterator path_ptr_array_iterator_type; typedef db::edge edge_type; + typedef db::edge_pair edge_pair_type; typedef db::text text_type; typedef db::text_ref text_ref_type; typedef db::text_ref text_ptr_type; @@ -126,18 +127,19 @@ public: SimplePolygonRef = 4, SimplePolygonPtrArray = 5, Edge = 6, - Path = 7, - PathRef = 8, - PathPtrArray = 9, - Box = 10, - BoxArray = 11, - ShortBox = 12, - ShortBoxArray = 13, - Text = 14, - TextRef = 15, - TextPtrArray = 16, - UserObject = 17, - Null = 18 // must be last! + EdgePair = 7, + Path = 8, + PathRef = 9, + PathPtrArray = 10, + Box = 11, + BoxArray = 12, + ShortBox = 13, + ShortBoxArray = 14, + Text = 15, + TextRef = 16, + TextPtrArray = 17, + UserObject = 18, + Null = 19 // must be last! }; enum flags_type @@ -151,7 +153,8 @@ public: | (1 << SimplePolygonRef) | (1 << SimplePolygonPtrArray), Edges = (1 << Edge), - Paths = (1 << Path) + EdgePairs = (1 << EdgePair), + Paths = (1 << Path) | (1 << PathRef) | (1 << PathPtrArray), Boxes = (1 << Box) @@ -352,14 +355,15 @@ private: char sz8 [sizeof (per_shape_iter_size )]; char sz9 [sizeof (per_shape_iter_size )]; char sz10 [sizeof (per_shape_iter_size )]; - char sz11 [sizeof (per_shape_iter_size )]; - char sz12 [sizeof (per_shape_iter_size )]; - char sz13 [sizeof (per_shape_iter_size )]; - char sz14 [sizeof (per_shape_iter_size )]; - char sz15 [sizeof (per_shape_iter_size )]; - char sz16 [sizeof (per_shape_iter_size )]; - char sz17 [sizeof (per_shape_iter_size )]; - char sz18 [sizeof (per_shape_iter_size )]; + char sz11 [sizeof (per_shape_iter_size )]; + char sz12 [sizeof (per_shape_iter_size )]; + char sz13 [sizeof (per_shape_iter_size )]; + char sz14 [sizeof (per_shape_iter_size )]; + char sz15 [sizeof (per_shape_iter_size )]; + char sz16 [sizeof (per_shape_iter_size )]; + char sz17 [sizeof (per_shape_iter_size )]; + char sz18 [sizeof (per_shape_iter_size )]; + char sz19 [sizeof (per_shape_iter_size )]; }; // this union is simply there to determine the maximum size required for all diff --git a/src/db/db/dbShapes2.cc b/src/db/db/dbShapes2.cc index 0d45fe433..cd545ee98 100644 --- a/src/db/db/dbShapes2.cc +++ b/src/db/db/dbShapes2.cc @@ -673,6 +673,12 @@ inline unsigned int iterator_type_mask (ShapeIterator::edge_type::tag) return 1 << ShapeIterator::Edge; } +/// @brief Internal: ShapeIterator masks per shape type +inline unsigned int iterator_type_mask (ShapeIterator::edge_pair_type::tag) +{ + return 1 << ShapeIterator::EdgePair; +} + /// @brief Internal: ShapeIterator masks per shape type inline unsigned int iterator_type_mask (ShapeIterator::path_type::tag) { @@ -917,6 +923,8 @@ template class layer_class template class layer_class, db::stable_layer_tag>; template class layer_class; template class layer_class, db::stable_layer_tag>; +template class layer_class; +template class layer_class, db::stable_layer_tag>; template class layer_class; template class layer_class, db::stable_layer_tag>; template class layer_class; @@ -953,6 +961,8 @@ template class layer_class, db::unstable_layer_tag>; template class layer_class; template class layer_class, db::unstable_layer_tag>; +template class layer_class; +template class layer_class, db::unstable_layer_tag>; template class layer_class; template class layer_class, db::unstable_layer_tag>; template class layer_class; diff --git a/src/db/db/dbShapes3.cc b/src/db/db/dbShapes3.cc index f4dd737b9..0f5737665 100644 --- a/src/db/db/dbShapes3.cc +++ b/src/db/db/dbShapes3.cc @@ -117,6 +117,8 @@ template DB_PUBLIC layer & template DB_PUBLIC layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); template DB_PUBLIC layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> (); +template DB_PUBLIC layer &Shapes::get_layer (); +template DB_PUBLIC layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); template DB_PUBLIC layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); @@ -153,6 +155,8 @@ template DB_PUBLIC layer template DB_PUBLIC layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); template DB_PUBLIC layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> (); +template DB_PUBLIC layer &Shapes::get_layer (); +template DB_PUBLIC layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); template DB_PUBLIC layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); @@ -190,6 +194,8 @@ template DB_PUBLIC const layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; template DB_PUBLIC const layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> () const; +template DB_PUBLIC const layer &Shapes::get_layer () const; +template DB_PUBLIC const layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; template DB_PUBLIC const layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; @@ -226,6 +232,8 @@ template DB_PUBLIC const layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; template DB_PUBLIC const layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> () const; +template DB_PUBLIC const layer &Shapes::get_layer () const; +template DB_PUBLIC const layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; template DB_PUBLIC const layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; @@ -282,6 +290,8 @@ Shapes::is_valid (const Shapes::shape_type &shape) const return is_valid_shape_by_tag (shape_type::simple_polygon_ptr_array_type::tag (), shape); case shape_type::Edge: return is_valid_shape_by_tag (shape_type::edge_type::tag (), shape); + case shape_type::EdgePair: + return is_valid_shape_by_tag (shape_type::edge_pair_type::tag (), shape); case shape_type::Path: return is_valid_shape_by_tag (shape_type::path_type::tag (), shape); case shape_type::PathRef: @@ -440,6 +450,9 @@ Shapes::erase_shape (const Shapes::shape_type &shape) case shape_type::Edge: erase_shape_by_tag (shape_type::edge_type::tag (), shape); break; + case shape_type::EdgePair: + erase_shape_by_tag (shape_type::edge_pair_type::tag (), shape); + break; case shape_type::Path: erase_shape_by_tag (shape_type::path_type::tag (), shape); break; @@ -532,6 +545,9 @@ Shapes::erase_shapes (const std::vector &shapes) case shape_type::Edge: erase_shapes_by_tag (shape_type::edge_type::tag (), s, snext); break; + case shape_type::EdgePair: + erase_shapes_by_tag (shape_type::edge_pair_type::tag (), s, snext); + break; case shape_type::Path: erase_shapes_by_tag (shape_type::path_type::tag (), s, snext); break; diff --git a/src/db/unit_tests/dbShapes.cc b/src/db/unit_tests/dbShapes.cc index 46df094d1..f7c3af9f1 100644 --- a/src/db/unit_tests/dbShapes.cc +++ b/src/db/unit_tests/dbShapes.cc @@ -172,6 +172,16 @@ std::string shapes_to_string_norm (tl::TestBase *_this, const db::Shapes &shapes r += "path " + p.to_string (); EXPECT_EQ (p.box ().to_string (), shape->bbox ().to_string ()); EXPECT_EQ (p.area (), shape->area ()); + } else if (shape->is_edge ()) { + db::Shape::edge_type p; + shape->edge (p); + r += "edge " + p.to_string (); + EXPECT_EQ (p.bbox ().to_string (), shape->bbox ().to_string ()); + } else if (shape->is_edge_pair ()) { + db::Shape::edge_pair_type p; + shape->edge_pair (p); + r += "edge_pair " + p.to_string (); + EXPECT_EQ (p.bbox ().to_string (), shape->bbox ().to_string ()); } else if (shape->is_text ()) { db::Shape::text_type p; shape->text (p); @@ -3263,6 +3273,42 @@ TEST(22) EXPECT_EQ (shapes.find (*s).to_string (), "null"); } +// Edge pairs +TEST(23) +{ + db::Manager m; + db::Shapes s (&m, 0, db::default_editable_mode ()); + db::Box b_empty; + + s.update_bbox (); + EXPECT_EQ (s.bbox (), b_empty); + + db::EdgePair ep (db::Edge (-100, -200, 0, 0), db::Edge (0, -100, 100, 100)); + s.insert (ep); + s.update_bbox (); + EXPECT_EQ (s.bbox (), db::Box (-100, -200, 100, 100)); + + db::ShapeIterator si = s.begin (db::ShapeIterator::EdgePairs); + EXPECT_EQ (!si.at_end (), true); + EXPECT_EQ (si->edge_pair ().to_string (), "(-100,-200;0,0)/(0,-100;100,100)"); + EXPECT_EQ (si->is_edge_pair (), true); + + db::EdgePair ep2; + si->instantiate (ep2); + EXPECT_EQ (ep2.to_string (), "(-100,-200;0,0)/(0,-100;100,100)"); + + ++si; + EXPECT_EQ (si.at_end (), true); + + db::Shapes s2 = s; + EXPECT_EQ (shapes_to_string_norm (_this, s2), "edge_pair (-100,-200;0,0)/(0,-100;100,100) #0\n"); + + s2.clear (); + s2.insert (db::EdgePairWithProperties (db::EdgePair (db::Edge (0, 0, 1, 1), db::Edge (10, 10, 11, 11)), 17)); + + EXPECT_EQ (shapes_to_string_norm (_this, s2), "edge_pair (0,0;1,1)/(10,10;11,11) #17\n"); +} + // Bug #107 TEST(100) { From 7a37da91e08c537da778ac0e0bcaad4ec0b416bf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 10 Nov 2018 00:07:53 +0100 Subject: [PATCH 047/335] EdgePairs refactoring - Uses a db::Shapes container - Aligned with db::Edges and db::Region - With original layer delegate --- src/db/db/db.pro | 14 +- src/db/db/dbAsIfFlatEdgePairs.cc | 276 ++++++++++++++ src/db/db/dbAsIfFlatEdgePairs.h | 87 +++++ src/db/db/dbEdgePairs.cc | 253 ++++++------- src/db/db/dbEdgePairs.h | 501 +++++++++++++++++++++----- src/db/db/dbEdgePairsDelegate.cc | 67 ++++ src/db/db/dbEdgePairsDelegate.h | 130 +++++++ src/db/db/dbEdges.h | 5 +- src/db/db/dbEmptyEdgePairs.cc | 99 +++++ src/db/db/dbEmptyEdgePairs.h | 84 +++++ src/db/db/dbFlatEdgePairs.cc | 202 +++++++++++ src/db/db/dbFlatEdgePairs.h | 168 +++++++++ src/db/db/dbOriginalLayerEdgePairs.cc | 196 ++++++++++ src/db/db/dbOriginalLayerEdgePairs.h | 75 ++++ src/db/db/dbRegion.h | 1 + src/db/db/dbTilingProcessor.h | 2 +- src/db/db/gsiDeclDbEdgePairs.cc | 17 +- src/db/db/gsiDeclDbShapes.cc | 65 +++- src/db/unit_tests/dbEdgePairs.cc | 3 +- src/rdb/rdb/gsiDeclRdb.cc | 2 +- 20 files changed, 1992 insertions(+), 255 deletions(-) create mode 100644 src/db/db/dbAsIfFlatEdgePairs.cc create mode 100644 src/db/db/dbAsIfFlatEdgePairs.h create mode 100644 src/db/db/dbEdgePairsDelegate.cc create mode 100644 src/db/db/dbEdgePairsDelegate.h create mode 100644 src/db/db/dbEmptyEdgePairs.cc create mode 100644 src/db/db/dbEmptyEdgePairs.h create mode 100644 src/db/db/dbFlatEdgePairs.cc create mode 100644 src/db/db/dbFlatEdgePairs.h create mode 100644 src/db/db/dbOriginalLayerEdgePairs.cc create mode 100644 src/db/db/dbOriginalLayerEdgePairs.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index b152e3b85..62e31b56a 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -130,7 +130,12 @@ SOURCES = \ dbAsIfFlatEdges.cc \ dbFlatEdges.cc \ dbEdgeBoolean.cc \ - dbOriginalLayerEdges.cc + dbOriginalLayerEdges.cc \ + dbAsIfFlatEdgePairs.cc \ + dbEmptyEdgePairs.cc \ + dbFlatEdgePairs.cc \ + dbOriginalLayerEdgePairs.cc \ + dbEdgePairsDelegate.cc HEADERS = \ dbArray.h \ @@ -230,7 +235,12 @@ HEADERS = \ dbAsIfFlatEdges.h \ dbFlatEdges.h \ dbEdgeBoolean.h \ - dbOriginalLayerEdges.h + dbOriginalLayerEdges.h \ + dbAsIfFlatEdgePairs.h \ + dbEmptyEdgePairs.h \ + dbFlatEdgePairs.h \ + dbOriginalLayerEdgePairs.h \ + dbEdgePairsDelegate.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc new file mode 100644 index 000000000..d23d308fa --- /dev/null +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -0,0 +1,276 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbAsIfFlatEdgePairs.h" +#include "dbFlatEdgePairs.h" +#include "dbFlatRegion.h" +#include "dbFlatEdges.h" +#include "dbEmptyEdgePairs.h" +#include "dbEdgePairs.h" +#include "dbBoxConvert.h" + +#include + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// AsIfFlagEdgePairs implementation + +AsIfFlatEdgePairs::AsIfFlatEdgePairs () + : EdgePairsDelegate (), m_bbox_valid (false) +{ + // .. nothing yet .. +} + +AsIfFlatEdgePairs::~AsIfFlatEdgePairs () +{ + // .. nothing yet .. +} + +std::string +AsIfFlatEdgePairs::to_string (size_t nmax) const +{ + std::ostringstream os; + EdgePairsIterator p (begin ()); + bool first = true; + for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { + if (! first) { + os << ";"; + } + first = false; + os << p->to_string (); + } + if (! p.at_end ()) { + os << "..."; + } + return os.str (); +} + +EdgePairsDelegate * +AsIfFlatEdgePairs::in (const EdgePairs &other, bool invert) const +{ + std::set op; + for (EdgePairsIterator o (other.begin ()); ! o.at_end (); ++o) { + op.insert (*o); + } + + std::auto_ptr new_edge_pairs (new FlatEdgePairs (false)); + + for (EdgePairsIterator o (begin ()); ! o.at_end (); ++o) { + if ((op.find (*o) == op.end ()) == invert) { + new_edge_pairs->insert (*o); + } + } + + return new_edge_pairs.release (); +} + +size_t +AsIfFlatEdgePairs::size () const +{ + size_t n = 0; + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + ++n; + } + return n; +} + +Box AsIfFlatEdgePairs::bbox () const +{ + if (! m_bbox_valid) { + m_bbox = compute_bbox (); + m_bbox_valid = true; + } + return m_bbox; +} + +Box AsIfFlatEdgePairs::compute_bbox () const +{ + db::Box b; + for (EdgePairsIterator e (begin ()); ! e.at_end (); ++e) { + b += e->bbox (); + } + return b; +} + +void AsIfFlatEdgePairs::update_bbox (const db::Box &b) +{ + m_bbox = b; + m_bbox_valid = true; +} + +void AsIfFlatEdgePairs::invalidate_bbox () +{ + m_bbox_valid = false; +} + +EdgePairsDelegate * +AsIfFlatEdgePairs::filtered (const EdgePairFilterBase &filter) const +{ + std::auto_ptr new_edge_pairs (new FlatEdgePairs ()); + + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_edge_pairs->insert (*p); + } + } + + return new_edge_pairs.release (); +} + +RegionDelegate * +AsIfFlatEdgePairs::polygons (db::Coord e) const +{ + std::auto_ptr output (new FlatRegion ()); + + for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { + db::Polygon poly = ep->normalized ().to_polygon (e); + if (poly.vertices () >= 3) { + output->insert (poly); + } + } + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdgePairs::edges () const +{ + std::auto_ptr output (new FlatEdges ()); + + for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { + output->insert (ep->first ()); + output->insert (ep->second ()); + } + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdgePairs::first_edges () const +{ + std::auto_ptr output (new FlatEdges ()); + + for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { + output->insert (ep->first ()); + } + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdgePairs::second_edges () const +{ + std::auto_ptr output (new FlatEdges ()); + + for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { + output->insert (ep->second ()); + } + + return output.release (); +} + +EdgePairsDelegate * +AsIfFlatEdgePairs::add (const EdgePairs &other) const +{ + FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + std::auto_ptr new_edge_pairs (new FlatEdgePairs (*other_flat)); + new_edge_pairs->invalidate_cache (); + + size_t n = new_edge_pairs->raw_edge_pairs ().size () + size (); + + new_edge_pairs->reserve (n); + + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } + + return new_edge_pairs.release (); + + } else { + + std::auto_ptr new_edge_pairs (new FlatEdgePairs ()); + + size_t n = size () + other.size (); + + new_edge_pairs->reserve (n); + + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } + for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } + + return new_edge_pairs.release (); + + } +} + +bool +AsIfFlatEdgePairs::equals (const EdgePairs &other) const +{ + if (empty () != other.empty ()) { + return false; + } + if (size () != other.size ()) { + return false; + } + EdgePairsIterator o1 (begin ()); + EdgePairsIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return false; + } + ++o1; + ++o2; + } + return true; +} + +bool +AsIfFlatEdgePairs::less (const EdgePairs &other) const +{ + if (empty () != other.empty ()) { + return empty () < other.empty (); + } + if (size () != other.size ()) { + return (size () < other.size ()); + } + EdgePairsIterator o1 (begin ()); + EdgePairsIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return *o1 < *o2; + } + ++o1; + ++o2; + } + return false; +} + +} + diff --git a/src/db/db/dbAsIfFlatEdgePairs.h b/src/db/db/dbAsIfFlatEdgePairs.h new file mode 100644 index 000000000..3913964cc --- /dev/null +++ b/src/db/db/dbAsIfFlatEdgePairs.h @@ -0,0 +1,87 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbAsIfFlatEdgePairs +#define HDR_dbAsIfFlatEdgePairs + +#include "dbCommon.h" + +#include "dbEdgePairsDelegate.h" + +namespace db { + +/** + * @brief Provides default flat implementations + */ +class DB_PUBLIC AsIfFlatEdgePairs + : public EdgePairsDelegate +{ +public: + AsIfFlatEdgePairs (); + virtual ~AsIfFlatEdgePairs (); + + virtual size_t size () const; + virtual std::string to_string (size_t) const; + virtual Box bbox () const; + + virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter) + { + return filtered (filter); + } + + virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const; + + virtual EdgePairsDelegate *add_in_place (const EdgePairs &other) + { + return add (other); + } + + virtual EdgePairsDelegate *add (const EdgePairs &other) const; + + virtual RegionDelegate *polygons (db::Coord e) const; + virtual EdgesDelegate *edges () const; + virtual EdgesDelegate *first_edges () const; + virtual EdgesDelegate *second_edges () const; + + virtual EdgePairsDelegate *in (const EdgePairs &, bool) const; + + virtual bool equals (const EdgePairs &other) const; + virtual bool less (const EdgePairs &other) const; + +protected: + void update_bbox (const db::Box &box); + void invalidate_bbox (); + +private: + AsIfFlatEdgePairs &operator= (const AsIfFlatEdgePairs &other); + + mutable bool m_bbox_valid; + mutable db::Box m_bbox; + + virtual db::Box compute_bbox () const; +}; + +} + +#endif + diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index 1ee65f466..7c0311fe7 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -24,6 +24,9 @@ #include "dbCommon.h" #include "dbEdgePairs.h" +#include "dbEmptyEdgePairs.h" +#include "dbFlatEdgePairs.h" +#include "dbOriginalLayerEdgePairs.h" #include "dbEdges.h" #include "dbRegion.h" @@ -34,164 +37,140 @@ namespace db { -void -EdgePairs::insert (const db::Edge &e1, const db::Edge &e2) +EdgePairs::EdgePairs () + : mp_delegate (new EmptyEdgePairs ()) { - m_edge_pairs.push_back (db::EdgePair (e1, e2)); - m_bbox_valid = false; + // .. nothing yet .. } -void -EdgePairs::insert (const edge_pair_type &ep) +EdgePairs::~EdgePairs () { - m_edge_pairs.push_back (ep); - m_bbox_valid = false; + delete mp_delegate; + mp_delegate = 0; } -bool -EdgePairs::operator== (const db::EdgePairs &other) const +EdgePairs::EdgePairs (EdgePairsDelegate *delegate) + : mp_delegate (delegate) { - if (empty () != other.empty ()) { - return false; - } - if (size () != other.size ()) { - return false; - } - db::EdgePairs::const_iterator o1 = begin (); - db::EdgePairs::const_iterator o2 = other.begin (); - while (o1 != end () && o2 != other.end ()) { - if (*o1 != *o2) { - return false; - } - ++o1; - ++o2; - } - return true; + // .. nothing yet .. } -bool -EdgePairs::operator< (const db::EdgePairs &other) const +EdgePairs::EdgePairs (const EdgePairs &other) + : mp_delegate (other.mp_delegate->clone ()) { - if (empty () != other.empty ()) { - return empty () < other.empty (); - } - if (size () != other.size ()) { - return (size () < other.size ()); - } - db::EdgePairs::const_iterator o1 = begin (); - db::EdgePairs::const_iterator o2 = other.begin (); - while (o1 != end () && o2 != other.end ()) { - if (*o1 != *o2) { - return *o1 < *o2; - } - ++o1; - ++o2; - } - return false; + // .. nothing yet .. } -db::EdgePairs & -EdgePairs::operator+= (const db::EdgePairs &other) +EdgePairs &EdgePairs::operator= (const EdgePairs &other) { - if (! other.empty ()) { - m_edge_pairs.insert (m_edge_pairs.end (), other.begin (), other.end ()); - m_bbox_valid = false; + if (this != &other) { + set_delegate (other.mp_delegate->clone ()); } return *this; } -std::string -EdgePairs::to_string (size_t nmax) const +EdgePairs::EdgePairs (const RecursiveShapeIterator &si) { - std::ostringstream os; - const_iterator ep; - for (ep = begin (); ep != end () && nmax != 0; ++ep, --nmax) { - if (ep != begin ()) { - os << ";"; + mp_delegate = new OriginalLayerEdgePairs (si); +} + +EdgePairs::EdgePairs (const RecursiveShapeIterator &si, const db::ICplxTrans &trans) +{ + mp_delegate = new OriginalLayerEdgePairs (si, trans); +} + +template +void EdgePairs::insert (const Sh &shape) +{ + flat_edge_pairs ()->insert (shape); +} + +template void EdgePairs::insert (const db::EdgePair &); + +void EdgePairs::insert (const db::Shape &shape) +{ + flat_edge_pairs ()->insert (shape); +} + +template +void EdgePairs::insert (const db::Shape &shape, const T &trans) +{ + flat_edge_pairs ()->insert (shape, trans); +} + +template void EdgePairs::insert (const db::Shape &, const db::ICplxTrans &); +template void EdgePairs::insert (const db::Shape &, const db::Trans &); + +void EdgePairs::clear () +{ + set_delegate (new EmptyEdgePairs ()); +} + +void EdgePairs::reserve (size_t n) +{ + flat_edge_pairs ()->reserve (n); +} + +template +EdgePairs &EdgePairs::transform (const T &trans) +{ + flat_edge_pairs ()->transform (trans); + return *this; +} + +// explicit instantiations +template EdgePairs &EdgePairs::transform (const db::ICplxTrans &); +template EdgePairs &EdgePairs::transform (const db::Trans &); + +const db::RecursiveShapeIterator & +EdgePairs::iter () const +{ + static db::RecursiveShapeIterator def_iter; + const db::RecursiveShapeIterator *i = mp_delegate->iter (); + return *(i ? i : &def_iter); +} + +void EdgePairs::polygons (Region &output, db::Coord e) const +{ + output.set_delegate (mp_delegate->polygons (e)); +} + +void EdgePairs::edges (Edges &output) const +{ + output.set_delegate (mp_delegate->edges ()); +} + +void EdgePairs::first_edges (Edges &output) const +{ + output.set_delegate (mp_delegate->first_edges ()); +} + +void EdgePairs::second_edges (Edges &output) const +{ + output.set_delegate (mp_delegate->second_edges ()); +} + +void EdgePairs::set_delegate (EdgePairsDelegate *delegate) +{ + if (delegate != mp_delegate) { + delete mp_delegate; + mp_delegate = delegate; + } +} + +FlatEdgePairs *EdgePairs::flat_edge_pairs () +{ + FlatEdgePairs *edge_pairs = dynamic_cast (mp_delegate); + if (! edge_pairs) { + edge_pairs = new FlatEdgePairs (); + if (mp_delegate) { + edge_pairs->EdgePairsDelegate::operator= (*mp_delegate); + edge_pairs->insert_seq (begin ()); } - os << ep->to_string (); + set_delegate (edge_pairs); } - if (ep != end ()) { - os << "..."; - } - return os.str (); -} -void -EdgePairs::clear () -{ - m_edge_pairs.clear (); - m_bbox = db::Box (); - m_bbox_valid = true; -} - -void -EdgePairs::polygons (Region &output, db::Coord e) const -{ - for (const_iterator ep = begin (); ep != end (); ++ep) { - db::Polygon poly = ep->normalized ().to_polygon (e); - if (poly.vertices () >= 3) { - output.insert (poly); - } - } -} - -void -EdgePairs::edges (Edges &output) const -{ - for (const_iterator ep = begin (); ep != end (); ++ep) { - output.insert (ep->first ()); - output.insert (ep->second ()); - } -} - -void -EdgePairs::first_edges (Edges &output) const -{ - for (const_iterator ep = begin (); ep != end (); ++ep) { - output.insert (ep->first ()); - } -} - -void -EdgePairs::second_edges (Edges &output) const -{ - for (const_iterator ep = begin (); ep != end (); ++ep) { - output.insert (ep->second ()); - } -} - -void -EdgePairs::init () -{ - m_bbox_valid = false; - m_report_progress = false; -} - -void -EdgePairs::ensure_bbox_valid () const -{ - if (! m_bbox_valid) { - m_bbox = db::Box (); - for (const_iterator ep = begin (); ep != end (); ++ep) { - m_bbox += db::Box (ep->first ().p1 (), ep->first ().p2 ()); - m_bbox += db::Box (ep->second ().p1 (), ep->second ().p2 ()); - } - m_bbox_valid = true; - } -} - -void -EdgePairs::disable_progress () -{ - m_report_progress = false; -} - -void -EdgePairs::enable_progress (const std::string &progress_desc) -{ - m_report_progress = true; - m_progress_desc = progress_desc; + return edge_pairs; } } diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index c0f7498f4..55b14bd8b 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -24,14 +24,195 @@ #ifndef HDR_dbEdgePairs #define HDR_dbEdgePairs -#include "dbEdge.h" -#include "dbEdgePair.h" +#include "dbEdgePairsDelegate.h" +#include "dbShape.h" +#include "dbRecursiveShapeIterator.h" + +#include "gsiObject.h" + +#include namespace db { -class Region; +class EdgePairFilterBase; +class FlatEdgePairs; +class EmptyEdgePairs; class Edges; +class Region; + +/** + * @brief An edge pair set iterator + * + * The iterator delivers the edge pairs of the edge pair set + */ +class DB_PUBLIC EdgePairsIterator +{ +public: + typedef EdgePairsIteratorDelegate::value_type value_type; + typedef const value_type &reference; + typedef const value_type *pointer; + typedef std::forward_iterator_tag iterator_category; + typedef void difference_type; + + /** + * @brief Default constructor + */ + EdgePairsIterator () + : mp_delegate (0) + { + // .. nothing yet .. + } + + /** + * @brief Constructor from a delegate + * The iterator will take ownership over the delegate + */ + EdgePairsIterator (EdgePairsIteratorDelegate *delegate) + : mp_delegate (delegate) + { + // .. nothing yet .. + } + + /** + * @brief Destructor + */ + ~EdgePairsIterator () + { + delete mp_delegate; + mp_delegate = 0; + } + + /** + * @brief Copy constructor and assignment + */ + EdgePairsIterator (const EdgePairsIterator &other) + : mp_delegate (0) + { + operator= (other); + } + + /** + * @brief Assignment + */ + EdgePairsIterator &operator= (const EdgePairsIterator &other) + { + if (this != &other) { + delete mp_delegate; + mp_delegate = other.mp_delegate ? other.mp_delegate->clone () : 0; + } + return *this; + } + + /** + * @Returns true, if the iterator is at the end + */ + bool at_end () const + { + return mp_delegate == 0 || mp_delegate->at_end (); + } + + /** + * @brief Increment + */ + EdgePairsIterator &operator++ () + { + if (mp_delegate) { + mp_delegate->increment (); + } + return *this; + } + + /** + * @brief Access + */ + reference operator* () const + { + const value_type *value = operator-> (); + tl_assert (value != 0); + return *value; + } + + /** + * @brief Access + */ + pointer operator-> () const + { + return mp_delegate ? mp_delegate->get () : 0; + } + +private: + EdgePairsIteratorDelegate *mp_delegate; +}; + +/** + * @brief A helper class allowing delivery of addressable edges + * + * In some applications (i.e. box scanner), edges need to be taken + * by address. The edge set cannot always deliver adressable edges. + * This class help providing this ability by keeping a temporary copy + * if required. + */ + +class DB_PUBLIC AddressableEdgePairDelivery +{ +public: + AddressableEdgePairDelivery () + : m_iter (), m_valid (false) + { + // .. nothing yet .. + } + + AddressableEdgePairDelivery (const EdgePairsIterator &iter, bool valid) + : m_iter (iter), m_valid (valid) + { + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + } + + bool at_end () const + { + return m_iter.at_end (); + } + + AddressableEdgePairDelivery &operator++ () + { + ++m_iter; + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + return *this; + } + + const db::EdgePair *operator-> () const + { + if (m_valid) { + return m_iter.operator-> (); + } else { + return &m_heap.back (); + } + } + +private: + EdgePairsIterator m_iter; + bool m_valid; + std::list m_heap; +}; + +class EdgePairs; + +/** + * @brief A base class for edge pair filters + */ +class DB_PUBLIC EdgePairFilterBase +{ +public: + EdgePairFilterBase () { } + virtual ~EdgePairFilterBase () { } + + virtual bool selected (const db::EdgePair &edge) const = 0; +}; /** * @brief A set of edge pairs @@ -46,92 +227,150 @@ class Edges; * can be converted to polygons or to individual edges. */ class DB_PUBLIC EdgePairs + : public gsi::ObjectBase { public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; typedef db::EdgePair edge_pair_type; - typedef std::vector::const_iterator const_iterator; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type distance_type; + typedef EdgePairsIterator const_iterator; /** * @brief Default constructor * * This constructor creates an empty edge pair set. */ - EdgePairs () - { - init (); - } + EdgePairs (); /** - * @brief Equality + * @brief Destructor */ - bool operator== (const db::EdgePairs &other) const; + ~EdgePairs (); /** - * @brief Inequality + * @brief Constructor from a delegate + * + * The region will take ownership of the delegate. */ - bool operator!= (const db::EdgePairs &other) const + EdgePairs (EdgePairsDelegate *delegate); + + /** + * @brief Copy constructor + */ + EdgePairs (const EdgePairs &other); + + /** + * @brief Assignment + */ + EdgePairs &operator= (const EdgePairs &other); + + /** + * @brief Constructor from an object + * + * Creates an edge pair set representing a single instance of that object + */ + template + EdgePairs (const Sh &s) + : mp_delegate (0) { - return !operator== (other); + insert (s); } /** - * @brief Less operator - */ - bool operator< (const db::EdgePairs &other) const; - - /** - * @brief Joining of edge pair sets + * @brief Sequence constructor * - * This method will combine the edge pairs from "other" with the egdes of "this". + * Creates an edge set from a sequence of objects. The objects need to be edge pairs. + * This version accepts iterators of the begin ... end style. */ - db::EdgePairs operator+ (const db::EdgePairs &other) const + template + EdgePairs (const Iter &b, const Iter &e) + : mp_delegate (0) { - db::EdgePairs d (*this); - d += other; - return d; + reserve (e - b); + for (Iter i = b; i != e; ++i) { + insert (*i); + } } /** - * @brief In-place joining of edge pair sets + * @brief Constructor from a RecursiveShapeIterator + * + * Creates an edge pair set from a recursive shape iterator. This allows to feed an edge pair set + * from a hierarchy of cells. */ - db::EdgePairs &operator+= (const db::EdgePairs &other); + EdgePairs (const RecursiveShapeIterator &si); /** - * @brief Begin iterator of the edge pair set + * @brief Constructor from a RecursiveShapeIterator with a transformation * - * The iterator delivers the edge pairs of the edge pair set. + * Creates an edge pair set from a recursive shape iterator. This allows to feed an edge pair set + * from a hierarchy of cells. The transformation is useful to scale to a specific + * DBU for example. + */ + EdgePairs (const RecursiveShapeIterator &si, const db::ICplxTrans &trans); + + /** + * @brief Gets the underlying delegate object + */ + EdgePairsDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Iterator of the edge pair set + * + * The iterator delivers the edges of the edge pair set. + * It follows the at_end semantics. */ const_iterator begin () const { - return m_edge_pairs.begin (); + return EdgePairsIterator (mp_delegate->begin ()); } /** - * @brief End iterator of the edge pair set - * - * The iterator delivers the edge pairs of the edge pair set. + * @brief Delivers a RecursiveShapeIterator pointing to the edge pairs plus the necessary transformation */ - const_iterator end () const + std::pair begin_iter () const { - return m_edge_pairs.end (); + return mp_delegate->begin_iter (); } /** - * @brief Insert an edge pair into the edge pair set + * @brief Inserts the given shape (working object) into the edge pair set */ - void insert (const db::Edge &e1, const db::Edge &e2); + template + void insert (const Sh &shape); /** - * @brief Insert an edge pair into the edge pair set + * @brief Inserts an edge pair made from two edges */ - void insert (const edge_pair_type &ep); + void insert (const db::Edge &e1, const db::Edge &e2) + { + insert (db::EdgePair (e1, e2)); + } + + /** + * @brief Insert a shape reference into the edge pair set + */ + void insert (const db::Shape &shape); + + /** + * @brief Insert a transformed shape into the edge pair set + */ + template + void insert (const db::Shape &shape, const T &trans); /** * @brief Returns true if the edge pair set is empty */ bool empty () const { - return m_edge_pairs.empty (); + return mp_delegate->empty (); } /** @@ -139,84 +378,65 @@ public: */ size_t size () const { - return m_edge_pairs.size (); + return mp_delegate->size (); } /** * @brief Returns a string representing the edge pair set + * + * nmax specifies how many edge pairs are included (set to std::numeric_limits::max() for "all". */ - std::string to_string (size_t nmax = 10) const; + std::string to_string (size_t nmax = 10) const + { + return mp_delegate->to_string (nmax); + } /** - * @brief Clear the edge pair set + * @brief Clears the edge set */ void clear (); /** - * @brief Reserve memory for the given number of polygons + * @brief Reserve memory for the given number of edges */ - void reserve (size_t n) - { - m_edge_pairs.reserve (n); - } + void reserve (size_t n); /** * @brief Returns the bounding box of the edge pair set */ Box bbox () const { - ensure_bbox_valid (); - return m_bbox; + return mp_delegate->bbox (); } /** - * @brief Filters the edge pair set + * @brief Filters the edge pairs * * This method will keep all edge pairs for which the filter returns true. + * Merged semantics applies. */ - template - EdgePairs &filter (F &filter) + EdgePairs &filter (const EdgePairFilterBase &filter) { - std::vector ::iterator ew = m_edge_pairs.begin (); - for (const_iterator e = begin (); e != end (); ++e) { - if (filter (*e)) { - *ew++ = *e; - } - } - m_edge_pairs.erase (ew, m_edge_pairs.end ()); + set_delegate (mp_delegate->filter_in_place (filter)); return *this; } /** * @brief Returns the filtered edge pairs * - * This method will return a new region with only those edge pairs which + * This method will return a new region with only those edge pairs which * conform to the filter criterion. */ - template - EdgePairs filtered (F &filter) const + EdgePairs filtered (const EdgePairFilterBase &filter) const { - EdgePairs d; - for (const_iterator e = begin (); e != end (); ++e) { - if (filter (*e)) { - d.insert (*e); - } - } - return d; + return EdgePairs (mp_delegate->filtered (filter)); } /** - * @brief Transform the edge pair set + * @brief Transforms the edge pair set */ template - EdgePairs &transform (const T &trans) - { - for (std::vector ::iterator e = m_edge_pairs.begin (); e != m_edge_pairs.end (); ++e) { - e->transform (trans); - } - m_bbox_valid = false; - return *this; - } + EdgePairs &transform (const T &trans); /** * @brief Returns the transformed edge pair set @@ -230,13 +450,110 @@ public: } /** - * @brief Swaps with the other edge pair set + * @brief Swaps with the other edge set */ void swap (db::EdgePairs &other) { - std::swap (m_edge_pairs, other.m_edge_pairs); - std::swap (m_bbox, other.m_bbox); - std::swap (m_bbox_valid, other.m_bbox_valid); + std::swap (other.mp_delegate, mp_delegate); + } + + /** + * @brief Joining of edge pair set + * + * This method joins the edge pair sets. + */ + EdgePairs operator+ (const EdgePairs &other) const + { + return EdgePairs (mp_delegate->add (other)); + } + + /** + * @brief In-place edge pair set joining + */ + EdgePairs &operator+= (const EdgePairs &other) + { + set_delegate (mp_delegate->add_in_place (other)); + return *this; + } + + /** + * @brief Returns all edge pairs which are in the other edge pair set + * + * This method will return all edge pairs which are part of another edge pair set. + * The match is done exactly. + * The "invert" flag can be used to invert the sense, i.e. with + * "invert" set to true, this method will return all edge pairs not + * in the other edge pair set. + */ + EdgePairs in (const EdgePairs &other, bool invert = false) const + { + return EdgePairs (mp_delegate->in (other, invert)); + } + + /** + * @brief Returns the nth edge pair + * + * This operation is only cheap if "has_valid_edge_pairs" is true. Otherwise, the + * complexity is O(n). + */ + const db::EdgePair *nth (size_t n) const + { + return mp_delegate->nth (n); + } + + /** + * @brief Returns true, if the edge pair set has valid edges stored within itself + * + * If the region has valid edges, it is permissable to use the edge pair's addresses + * from the iterator. Furthermore, the random access operator nth() is available. + */ + bool has_valid_edge_pairs () const + { + return mp_delegate->has_valid_edge_pairs (); + } + + /** + * @brief Returns an addressable delivery for edge pairs + * + * This object allows accessing the edge pairs by address, even if they + * are not delivered from a container. The magic is a heap object + * inside the delivery object. Hence, the deliver object must persist + * as long as the addresses are required. + */ + AddressableEdgePairDelivery addressable_edge_pairs () const + { + return AddressableEdgePairDelivery (begin (), has_valid_edge_pairs ()); + } + + /** + * @brief Gets the internal iterator + * + * This method is intended for users who know what they are doing + */ + const db::RecursiveShapeIterator &iter () const; + + /** + * @brief Equality + */ + bool operator== (const db::EdgePairs &other) const + { + return mp_delegate->equals (other); + } + + /** + * @brief Inequality + */ + bool operator!= (const db::EdgePairs &other) const + { + return ! mp_delegate->equals (other); + } + + /** + * @brief Less operator + */ + bool operator< (const db::EdgePairs &other) const + { + return mp_delegate->less (other); } /** @@ -288,22 +605,24 @@ public: * * @param progress_text The description text of the progress object */ - void enable_progress (const std::string &progress_desc = std::string ()); + void enable_progress (const std::string &progress_desc = std::string ()) + { + mp_delegate->enable_progress (progress_desc); + } /** * @brief Disable progress reporting */ - void disable_progress (); + void disable_progress () + { + mp_delegate->disable_progress (); + } private: - std::vector m_edge_pairs; - mutable db::Box m_bbox; - mutable bool m_bbox_valid; - bool m_report_progress; - std::string m_progress_desc; + EdgePairsDelegate *mp_delegate; - void init (); - void ensure_bbox_valid () const; + void set_delegate (EdgePairsDelegate *delegate); + FlatEdgePairs *flat_edge_pairs (); }; } diff --git a/src/db/db/dbEdgePairsDelegate.cc b/src/db/db/dbEdgePairsDelegate.cc new file mode 100644 index 000000000..5f83bb58f --- /dev/null +++ b/src/db/db/dbEdgePairsDelegate.cc @@ -0,0 +1,67 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbEdgePairsDelegate.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- + +EdgePairsDelegate::EdgePairsDelegate () +{ + m_report_progress = false; +} + +EdgePairsDelegate::EdgePairsDelegate (const EdgePairsDelegate &other) +{ + operator= (other); +} + +EdgePairsDelegate & +EdgePairsDelegate::operator= (const EdgePairsDelegate &other) +{ + if (this != &other) { + m_report_progress = other.m_report_progress; + } + return *this; +} + +EdgePairsDelegate::~EdgePairsDelegate () +{ + // .. nothing yet .. +} + +void EdgePairsDelegate::enable_progress (const std::string &progress_desc) +{ + m_report_progress = true; + m_progress_desc = progress_desc; +} + +void EdgePairsDelegate::disable_progress () +{ + m_report_progress = false; +} + +} + diff --git a/src/db/db/dbEdgePairsDelegate.h b/src/db/db/dbEdgePairsDelegate.h new file mode 100644 index 000000000..8f979e4d1 --- /dev/null +++ b/src/db/db/dbEdgePairsDelegate.h @@ -0,0 +1,130 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbEdgePairsDelegate +#define HDR_dbEdgePairsDelegate + +#include "dbCommon.h" + +#include "dbEdgePair.h" + +namespace db { + +class RecursiveShapeIterator; +class EdgePairs; +class EdgePairFilterBase; +class RegionDelegate; +class EdgesDelegate; + +/** + * @brief The edge pair set iterator delegate + */ +class DB_PUBLIC EdgePairsIteratorDelegate +{ +public: + EdgePairsIteratorDelegate () { } + virtual ~EdgePairsIteratorDelegate () { } + + typedef db::EdgePair value_type; + + virtual bool at_end () const = 0; + virtual void increment () = 0; + virtual const value_type *get () const = 0; + virtual EdgePairsIteratorDelegate *clone () const = 0; +}; + +/** + * @brief The delegate for the actual edge set implementation + */ +class DB_PUBLIC EdgePairsDelegate +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::EdgePair edge_pair_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + + EdgePairsDelegate (); + virtual ~EdgePairsDelegate (); + + EdgePairsDelegate (const EdgePairsDelegate &other); + EdgePairsDelegate &operator= (const EdgePairsDelegate &other); + + virtual EdgePairsDelegate *clone () const = 0; + + void enable_progress (const std::string &progress_desc); + void disable_progress (); + + virtual std::string to_string (size_t nmax) const = 0; + + virtual EdgePairsIteratorDelegate *begin () const = 0; + virtual std::pair begin_iter () const = 0; + + virtual bool empty () const = 0; + virtual size_t size () const = 0; + + virtual Box bbox () const = 0; + + virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter) = 0; + virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &filter) const = 0; + + virtual RegionDelegate *polygons (db::Coord e) const = 0; + virtual EdgesDelegate *edges () const = 0; + virtual EdgesDelegate *first_edges () const = 0; + virtual EdgesDelegate *second_edges () const = 0; + + virtual EdgePairsDelegate *add_in_place (const EdgePairs &other) = 0; + virtual EdgePairsDelegate *add (const EdgePairs &other) const = 0; + + virtual EdgePairsDelegate *in (const EdgePairs &other, bool invert) const = 0; + + virtual const db::EdgePair *nth (size_t n) const = 0; + virtual bool has_valid_edge_pairs () const = 0; + + virtual const db::RecursiveShapeIterator *iter () const = 0; + + virtual bool equals (const EdgePairs &other) const = 0; + virtual bool less (const EdgePairs &other) const = 0; + +protected: + const std::string &progress_desc () const + { + return m_progress_desc; + } + + bool report_progress () const + { + return m_report_progress; + } + +private: + bool m_report_progress; + std::string m_progress_desc; +}; + +} + +#endif + diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 1236b5e05..d3d6f9333 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -37,7 +37,6 @@ class EdgeFilterBase; class FlatEdges; class EmptyEdges; - /** * @brief An edge set iterator * @@ -200,7 +199,7 @@ private: class Edges; /** - * @brief A base class for polygon filters + * @brief A base class for edge filters */ class DB_PUBLIC EdgeFilterBase { @@ -1256,6 +1255,8 @@ public: } private: + friend class EdgePairs; + EdgesDelegate *mp_delegate; void set_delegate (EdgesDelegate *delegate); diff --git a/src/db/db/dbEmptyEdgePairs.cc b/src/db/db/dbEmptyEdgePairs.cc new file mode 100644 index 000000000..e806c34f6 --- /dev/null +++ b/src/db/db/dbEmptyEdgePairs.cc @@ -0,0 +1,99 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbEmptyEdgePairs.h" +#include "dbEmptyRegion.h" +#include "dbEmptyEdges.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- + +EmptyEdgePairs::EmptyEdgePairs () +{ + // .. nothing yet .. +} + +EmptyEdgePairs::EmptyEdgePairs (const EmptyEdgePairs &other) + : EdgePairsDelegate (other) +{ + // .. nothing yet .. +} + +EdgePairsDelegate * +EmptyEdgePairs::clone () const +{ + return new EmptyEdgePairs (*this); +} + +RegionDelegate * +EmptyEdgePairs::polygons (db::Coord) const +{ + return new EmptyRegion (); +} + +EdgesDelegate * +EmptyEdgePairs::edges () const +{ + return new EmptyEdges (); +} + +EdgesDelegate * +EmptyEdgePairs::first_edges () const +{ + return new EmptyEdges (); +} + +EdgesDelegate * +EmptyEdgePairs::second_edges () const +{ + return new EmptyEdges (); +} + +EdgePairsDelegate * +EmptyEdgePairs::add_in_place (const EdgePairs &other) +{ + return add (other); +} + +EdgePairsDelegate * +EmptyEdgePairs::add (const EdgePairs &other) const +{ + return other.delegate ()->clone (); +} + +bool +EmptyEdgePairs::equals (const EdgePairs &other) const +{ + return other.empty (); +} + +bool +EmptyEdgePairs::less (const EdgePairs &other) const +{ + return other.empty () ? false : true; +} + +} + diff --git a/src/db/db/dbEmptyEdgePairs.h b/src/db/db/dbEmptyEdgePairs.h new file mode 100644 index 000000000..01281d8d6 --- /dev/null +++ b/src/db/db/dbEmptyEdgePairs.h @@ -0,0 +1,84 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbEmptyEdgePairs +#define HDR_dbEmptyEdgePairs + +#include "dbCommon.h" + +#include "dbEdgePairsDelegate.h" +#include "dbRecursiveShapeIterator.h" + +namespace db { + +/** + * @brief The delegate for the actual edge set implementation + */ +class DB_PUBLIC EmptyEdgePairs + : public EdgePairsDelegate +{ +public: + EmptyEdgePairs (); + EmptyEdgePairs (const EmptyEdgePairs &other); + + virtual EdgePairsDelegate *clone () const; + + virtual std::string to_string (size_t) const { return std::string (); } + + virtual EdgePairsIteratorDelegate *begin () const { return 0; } + virtual std::pair begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + + virtual bool empty () const { return true; } + virtual size_t size () const { return 0; } + + virtual Box bbox () const { return Box (); } + + virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &) { return this; } + virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const { return new EmptyEdgePairs (); } + + virtual RegionDelegate *polygons (db::Coord e) const; + virtual EdgesDelegate *edges () const; + virtual EdgesDelegate *first_edges () const; + virtual EdgesDelegate *second_edges () const; + + virtual EdgePairsDelegate *add_in_place (const EdgePairs &other); + virtual EdgePairsDelegate *add (const EdgePairs &other) const; + + virtual EdgePairsDelegate *in (const EdgePairs &, bool) const { return new EmptyEdgePairs (); } + + virtual const db::EdgePair *nth (size_t) const { tl_assert (false); } + virtual bool has_valid_edge_pairs () const { return true; } + + virtual const db::RecursiveShapeIterator *iter () const { return 0; } + + virtual bool equals (const EdgePairs &other) const; + virtual bool less (const EdgePairs &other) const; + +private: + EmptyEdgePairs &operator= (const EmptyEdgePairs &other); +}; + +} + +#endif + diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc new file mode 100644 index 000000000..2db48f554 --- /dev/null +++ b/src/db/db/dbFlatEdgePairs.cc @@ -0,0 +1,202 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbFlatEdgePairs.h" +#include "dbEmptyEdgePairs.h" +#include "dbEdgePairs.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// FlatEdgePairs implementation + +FlatEdgePairs::FlatEdgePairs () + : AsIfFlatEdgePairs (), m_edge_pairs (false) +{ + // .. nothing yet .. +} + +FlatEdgePairs::~FlatEdgePairs () +{ + // .. nothing yet .. +} + +FlatEdgePairs::FlatEdgePairs (const FlatEdgePairs &other) + : AsIfFlatEdgePairs (other), m_edge_pairs (false) +{ + m_edge_pairs = other.m_edge_pairs; +} + +FlatEdgePairs::FlatEdgePairs (const db::Shapes &edge_pairs) + : AsIfFlatEdgePairs (), m_edge_pairs (edge_pairs) +{ + // .. nothing yet .. +} + +void FlatEdgePairs::invalidate_cache () +{ + invalidate_bbox (); +} + +void FlatEdgePairs::reserve (size_t n) +{ + m_edge_pairs.reserve (db::EdgePair::tag (), n); +} + +EdgePairsIteratorDelegate *FlatEdgePairs::begin () const +{ + return new FlatEdgePairsIterator (m_edge_pairs.get_layer ().begin (), m_edge_pairs.get_layer ().end ()); +} + +std::pair FlatEdgePairs::begin_iter () const +{ + return std::make_pair (db::RecursiveShapeIterator (m_edge_pairs), db::ICplxTrans ()); +} + +bool FlatEdgePairs::empty () const +{ + return m_edge_pairs.empty (); +} + +size_t FlatEdgePairs::size () const +{ + return m_edge_pairs.size (); +} + +Box FlatEdgePairs::compute_bbox () const +{ + m_edge_pairs.update_bbox (); + return m_edge_pairs.bbox (); +} + +EdgePairsDelegate * +FlatEdgePairs::filter_in_place (const EdgePairFilterBase &filter) +{ + edge_pair_iterator_type pw = m_edge_pairs.get_layer ().begin (); + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + if (pw == m_edge_pairs.get_layer ().end ()) { + m_edge_pairs.get_layer ().insert (*p); + pw = m_edge_pairs.get_layer ().end (); + } else { + m_edge_pairs.get_layer ().replace (pw++, *p); + } + } + } + + m_edge_pairs.get_layer ().erase (pw, m_edge_pairs.get_layer ().end ()); + + return this; +} + +EdgePairsDelegate *FlatEdgePairs::add (const EdgePairs &other) const +{ + std::auto_ptr new_edge_pairs (new FlatEdgePairs (*this)); + new_edge_pairs->invalidate_cache (); + + FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + new_edge_pairs->raw_edge_pairs ().insert (other_flat->raw_edge_pairs ().get_layer ().begin (), other_flat->raw_edge_pairs ().get_layer ().end ()); + + } else { + + size_t n = new_edge_pairs->raw_edge_pairs ().size (); + for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + new_edge_pairs->raw_edge_pairs ().reserve (db::EdgePair::tag (), n); + + for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } + + } + + return new_edge_pairs.release (); +} + +EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other) +{ + invalidate_cache (); + + FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + m_edge_pairs.insert (other_flat->raw_edge_pairs ().get_layer ().begin (), other_flat->raw_edge_pairs ().get_layer ().end ()); + + } else { + + size_t n = m_edge_pairs.size (); + for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + m_edge_pairs.reserve (db::EdgePair::tag (), n); + + for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { + m_edge_pairs.insert (*p); + } + + } + + return this; +} + +const db::EdgePair *FlatEdgePairs::nth (size_t n) const +{ + return n < m_edge_pairs.size () ? &m_edge_pairs.get_layer ().begin () [n] : 0; +} + +bool FlatEdgePairs::has_valid_edge_pairs () const +{ + return true; +} + +const db::RecursiveShapeIterator *FlatEdgePairs::iter () const +{ + return 0; +} + +void +FlatEdgePairs::insert (const db::EdgePair &ep) +{ + m_edge_pairs.insert (ep); +} + +void +FlatEdgePairs::insert (const db::Shape &shape) +{ + if (shape.is_edge_pair ()) { + + db::EdgePair ep; + shape.edge_pair (ep); + insert (ep); + + } +} + +} + diff --git a/src/db/db/dbFlatEdgePairs.h b/src/db/db/dbFlatEdgePairs.h new file mode 100644 index 000000000..c0c2d96f2 --- /dev/null +++ b/src/db/db/dbFlatEdgePairs.h @@ -0,0 +1,168 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbFlatEdgePairs +#define HDR_dbFlatEdgePairs + +#include "dbCommon.h" + +#include "dbAsIfFlatEdgePairs.h" +#include "dbShapes.h" + +namespace db { + +/** + * @brief An iterator delegate for the flat edge pair set + */ +class DB_PUBLIC FlatEdgePairsIterator + : public EdgePairsIteratorDelegate +{ +public: + typedef db::layer edge_pair_layer_type; + typedef edge_pair_layer_type::iterator iterator_type; + + FlatEdgePairsIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // .. nothing yet .. + } + + virtual bool at_end () const + { + return m_from == m_to; + } + + virtual void increment () + { + ++m_from; + } + + virtual const value_type *get () const + { + return m_from.operator-> (); + } + + virtual EdgePairsIteratorDelegate *clone () const + { + return new FlatEdgePairsIterator (*this); + } + +private: + friend class EdgePairs; + + iterator_type m_from, m_to; +}; + +/** + * @brief The delegate for the actual edge pair set implementation + */ +class DB_PUBLIC FlatEdgePairs + : public AsIfFlatEdgePairs +{ +public: + typedef db::layer edge_pair_layer_type; + typedef edge_pair_layer_type::iterator edge_pair_iterator_type; + + FlatEdgePairs (); + FlatEdgePairs (const db::Shapes &edges); + + FlatEdgePairs (const FlatEdgePairs &other); + + virtual ~FlatEdgePairs (); + + EdgePairsDelegate *clone () const + { + return new FlatEdgePairs (*this); + } + + void reserve (size_t); + + virtual EdgePairsIteratorDelegate *begin () const; + virtual std::pair begin_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + + virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter); + + virtual EdgePairsDelegate *add_in_place (const EdgePairs &other); + virtual EdgePairsDelegate *add (const EdgePairs &other) const; + + virtual const db::EdgePair *nth (size_t n) const; + virtual bool has_valid_edge_pairs () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + void insert (const db::EdgePair &edge_pair); + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_edge_pair ()) { + + db::EdgePair ep; + shape.edge_pair (ep); + ep.transform (trans); + insert (ep); + + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } + + template + void transform (const Trans &trans) + { + if (! trans.is_unity ()) { + for (edge_pair_iterator_type p = m_edge_pairs.template get_layer ().begin (); p != m_edge_pairs.template get_layer ().end (); ++p) { + m_edge_pairs.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } + +protected: + virtual Box compute_bbox () const; + void invalidate_cache (); + +private: + friend class AsIfFlatEdgePairs; + + db::Shapes &raw_edge_pairs () { return m_edge_pairs; } + + FlatEdgePairs &operator= (const FlatEdgePairs &other); + + mutable db::Shapes m_edge_pairs; +}; + +} + +#endif + diff --git a/src/db/db/dbOriginalLayerEdgePairs.cc b/src/db/db/dbOriginalLayerEdgePairs.cc new file mode 100644 index 000000000..14e92019a --- /dev/null +++ b/src/db/db/dbOriginalLayerEdgePairs.cc @@ -0,0 +1,196 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbOriginalLayerEdgePairs.h" +#include "dbEdgePairs.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// OriginalLayerEdgePairs implementation + +namespace +{ + + class OriginalLayerEdgePairsIterator + : public EdgePairsIteratorDelegate + { + public: + OriginalLayerEdgePairsIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) + : m_rec_iter (iter), m_iter_trans (trans) + { + set (); + } + + virtual bool at_end () const + { + return m_rec_iter.at_end (); + } + + virtual void increment () + { + inc (); + set (); + } + + virtual const value_type *get () const + { + return &m_edge_pair; + } + + virtual EdgePairsIteratorDelegate *clone () const + { + return new OriginalLayerEdgePairsIterator (*this); + } + + private: + friend class EdgePairs; + + db::RecursiveShapeIterator m_rec_iter; + db::ICplxTrans m_iter_trans; + db::EdgePair m_edge_pair; + + void set () + { + while (! m_rec_iter.at_end () && ! m_rec_iter.shape ().is_edge_pair ()) { + ++m_rec_iter; + } + if (! m_rec_iter.at_end ()) { + m_rec_iter.shape ().edge_pair (m_edge_pair); + m_edge_pair.transform (m_iter_trans * m_rec_iter.trans ()); + } + } + + void inc () + { + if (! m_rec_iter.at_end ()) { + ++m_rec_iter; + } + } + }; + +} + +OriginalLayerEdgePairs::OriginalLayerEdgePairs () + : AsIfFlatEdgePairs () +{ + init (); +} + +OriginalLayerEdgePairs::OriginalLayerEdgePairs (const OriginalLayerEdgePairs &other) + : AsIfFlatEdgePairs (other), + m_iter (other.m_iter), + m_iter_trans (other.m_iter_trans) +{ + // .. nothing yet .. +} + +OriginalLayerEdgePairs::OriginalLayerEdgePairs (const RecursiveShapeIterator &si) + : AsIfFlatEdgePairs (), m_iter (si) +{ + init (); +} + +OriginalLayerEdgePairs::OriginalLayerEdgePairs (const RecursiveShapeIterator &si, const db::ICplxTrans &trans) + : AsIfFlatEdgePairs (), m_iter (si), m_iter_trans (trans) +{ + init (); +} + +OriginalLayerEdgePairs::~OriginalLayerEdgePairs () +{ + // .. nothing yet .. +} + +EdgePairsDelegate * +OriginalLayerEdgePairs::clone () const +{ + return new OriginalLayerEdgePairs (*this); +} + +EdgePairsIteratorDelegate * +OriginalLayerEdgePairs::begin () const +{ + return new OriginalLayerEdgePairsIterator (m_iter, m_iter_trans); +} + +std::pair +OriginalLayerEdgePairs::begin_iter () const +{ + return std::make_pair (m_iter, m_iter_trans); +} + +bool +OriginalLayerEdgePairs::empty () const +{ + return m_iter.at_end (); +} + +const db::EdgePair * +OriginalLayerEdgePairs::nth (size_t) const +{ + tl_assert (false); +} + +bool +OriginalLayerEdgePairs::has_valid_edge_pairs () const +{ + return false; +} + +const db::RecursiveShapeIterator * +OriginalLayerEdgePairs::iter () const +{ + return &m_iter; +} + +bool +OriginalLayerEdgePairs::equals (const EdgePairs &other) const +{ + const OriginalLayerEdgePairs *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return true; + } else { + return AsIfFlatEdgePairs::equals (other); + } +} + +bool +OriginalLayerEdgePairs::less (const EdgePairs &other) const +{ + const OriginalLayerEdgePairs *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return false; + } else { + return AsIfFlatEdgePairs::less (other); + } +} + +void +OriginalLayerEdgePairs::init () +{ + // .. nothing yet .. +} + +} diff --git a/src/db/db/dbOriginalLayerEdgePairs.h b/src/db/db/dbOriginalLayerEdgePairs.h new file mode 100644 index 000000000..9d7ada01c --- /dev/null +++ b/src/db/db/dbOriginalLayerEdgePairs.h @@ -0,0 +1,75 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbOriginalLayerEdgePairs +#define HDR_dbOriginalLayerEdgePairs + +#include "dbCommon.h" + +#include "dbAsIfFlatEdgePairs.h" +#include "dbShapes.h" +#include "dbRecursiveShapeIterator.h" + +namespace db { + +/** + * @brief An original layerregion based on a RecursiveShapeIterator + */ +class DB_PUBLIC OriginalLayerEdgePairs + : public AsIfFlatEdgePairs +{ +public: + OriginalLayerEdgePairs (); + OriginalLayerEdgePairs (const OriginalLayerEdgePairs &other); + OriginalLayerEdgePairs (const RecursiveShapeIterator &si); + OriginalLayerEdgePairs (const RecursiveShapeIterator &si, const db::ICplxTrans &trans); + virtual ~OriginalLayerEdgePairs (); + + EdgePairsDelegate *clone () const; + + virtual EdgePairsIteratorDelegate *begin () const; + virtual std::pair begin_iter () const; + + virtual bool empty () const; + + virtual const db::EdgePair *nth (size_t n) const; + virtual bool has_valid_edge_pairs () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const EdgePairs &other) const; + virtual bool less (const EdgePairs &other) const; + +private: + OriginalLayerEdgePairs &operator= (const OriginalLayerEdgePairs &other); + + mutable db::RecursiveShapeIterator m_iter; + db::ICplxTrans m_iter_trans; + + void init (); +}; + +} + +#endif + diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index b758f4488..3ef7d0223 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1660,6 +1660,7 @@ public: private: friend class Edges; + friend class EdgePairs; RegionDelegate *mp_delegate; diff --git a/src/db/db/dbTilingProcessor.h b/src/db/db/dbTilingProcessor.h index acedad2c5..9ac8dcb57 100644 --- a/src/db/db/dbTilingProcessor.h +++ b/src/db/db/dbTilingProcessor.h @@ -341,7 +341,7 @@ void insert (X &inserter, const db::Edges &data, const db::Box &tile, bool clip) template void insert (X &inserter, const db::EdgePairs &data, const db::Box &tile, bool clip) { - for (db::EdgePairs::const_iterator o = data.begin (); o != data.end (); ++o) { + for (db::EdgePairs::const_iterator o = data.begin (); ! o.at_end (); ++o) { insert (inserter, *o, tile, clip); } } diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 34eda041e..03df7340b 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -45,15 +45,6 @@ static std::string to_string1 (const db::EdgePairs *r, size_t n) return r->to_string (n); } -static const db::EdgePair *nth (const db::EdgePairs *r, size_t n) -{ - if (n >= r->size ()) { - return 0; - } else { - return &r->begin ()[n]; - } -} - static db::EdgePairs &move_p (db::EdgePairs *r, const db::Vector &p) { r->transform (db::Disp (p)); @@ -94,7 +85,7 @@ static db::Region extents2 (const db::EdgePairs *r, db::Coord dx, db::Coord dy) { db::Region e; e.reserve (r->size ()); - for (db::EdgePairs::const_iterator i = r->begin (); i != r->end (); ++i) { + for (db::EdgePairs::const_iterator i = r->begin (); ! i.at_end (); ++i) { e.insert (i->bbox ().enlarged (db::Vector (dx, dy))); } return e; @@ -133,7 +124,7 @@ static db::Edges second_edges (const db::EdgePairs *ep) static void insert_e (db::EdgePairs *e, const db::EdgePairs &a) { - for (db::EdgePairs::const_iterator p = a.begin (); p != a.end (); ++p) { + for (db::EdgePairs::const_iterator p = a.begin (); ! p.at_end (); ++p) { e->insert (*p); } } @@ -339,10 +330,10 @@ Class decl_EdgePairs ("db", "EdgePairs", method ("size", &db::EdgePairs::size, "@brief Returns the number of edge pairs in this collection\n" ) + - gsi::iterator ("each", &db::EdgePairs::begin, &db::EdgePairs::end, + gsi::iterator ("each", &db::EdgePairs::begin, "@brief Returns each edge pair of the edge pair collection\n" ) + - method_ext ("[]", &nth, + method ("[]", &db::EdgePairs::nth, "@brief Returns the nth edge pair\n" "@args n\n" "\n" diff --git a/src/db/db/gsiDeclDbShapes.cc b/src/db/db/gsiDeclDbShapes.cc index ec8dd1408..24291e3cf 100644 --- a/src/db/db/gsiDeclDbShapes.cc +++ b/src/db/db/gsiDeclDbShapes.cc @@ -338,7 +338,7 @@ static void insert_edges_with_dtrans (db::Shapes *sh, const db::Edges &r, const static void insert_edge_pairs_as_polygons (db::Shapes *sh, const db::EdgePairs &r, db::Coord e) { - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->normalized ().to_simple_polygon (e)); } } @@ -346,14 +346,14 @@ static void insert_edge_pairs_as_polygons (db::Shapes *sh, const db::EdgePairs & static void insert_edge_pairs_as_polygons_d (db::Shapes *sh, const db::EdgePairs &r, db::DCoord de) { db::Coord e = db::coord_traits::rounded (de / shapes_dbu (sh)); - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->normalized ().to_simple_polygon (e)); } } static void insert_edge_pairs_as_polygons_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans, db::Coord e) { - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->normalized ().to_simple_polygon (e).transformed (trans)); } } @@ -363,14 +363,14 @@ static void insert_edge_pairs_as_polygons_with_dtrans (db::Shapes *sh, const db: db::Coord e = db::coord_traits::rounded (de / shapes_dbu (sh)); db::CplxTrans dbu_trans (shapes_dbu (sh)); db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->normalized ().to_simple_polygon (e).transformed (itrans)); } } static void insert_edge_pairs_as_edges (db::Shapes *sh, const db::EdgePairs &r) { - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->first ()); sh->insert (s->second ()); } @@ -378,7 +378,7 @@ static void insert_edge_pairs_as_edges (db::Shapes *sh, const db::EdgePairs &r) static void insert_edge_pairs_as_edges_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans) { - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->first ().transformed (trans)); sh->insert (s->second ().transformed (trans)); } @@ -388,12 +388,35 @@ static void insert_edge_pairs_as_edges_with_dtrans (db::Shapes *sh, const db::Ed { db::CplxTrans dbu_trans (shapes_dbu (sh)); db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->first ().transformed (itrans)); sh->insert (s->second ().transformed (itrans)); } } +static void insert_edge_pairs (db::Shapes *sh, const db::EdgePairs &r) +{ + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { + sh->insert (*s); + } +} + +static void insert_edge_pairs_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans) +{ + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { + sh->insert (s->transformed (trans)); + } +} + +static void insert_edge_pairs_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans) +{ + db::CplxTrans dbu_trans (shapes_dbu (sh)); + db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { + sh->insert (s->transformed (itrans)); + } +} + static unsigned int s_all () { return db::ShapeIterator::All; } static unsigned int s_all_with_properties () { return db::ShapeIterator::AllWithProperties; } static unsigned int s_properties () { return db::ShapeIterator::Properties; } @@ -551,6 +574,34 @@ Class decl_Shapes ("db", "Shapes", "\n" "This method has been introduced in version 0.25.\n" ) + + gsi::method_ext ("insert", &insert_edge_pairs, gsi::arg ("edge_pairs"), + "@brief Inserts the edges from the edge pair collection into this shape container\n" + "@param edges The edge pairs to insert\n" + "\n" + "This method inserts all edge pairs from the edge pair collection into this shape container.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("insert", &insert_edge_pairs_with_trans, gsi::arg ("edge_pairs"), gsi::arg ("trans"), + "@brief Inserts the edge pairs from the edge pair collection into this shape container with a transformation\n" + "@param edges The edge pairs to insert\n" + "@param trans The transformation to apply\n" + "\n" + "This method inserts all edge pairs from the edge pair collection into this shape container.\n" + "Before an edge pair is inserted, the given transformation is applied.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("insert", &insert_edge_pairs_with_dtrans, gsi::arg ("edge_pairs"), gsi::arg ("trans"), + "@brief Inserts the edge pairs from the edge pair collection into this shape container with a transformation (given in micrometer units)\n" + "@param edges The edge pairs to insert\n" + "@param trans The transformation to apply (displacement in micrometer units)\n" + "\n" + "This method inserts all edge pairs from the edge collection into this shape container.\n" + "Before an edge pair is inserted, the given transformation is applied.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + gsi::method_ext ("insert_as_polygons", &insert_edge_pairs_as_polygons, gsi::arg ("edge_pairs"), gsi::arg ("e"), "@brief Inserts the edge pairs from the edge pair collection as polygons into this shape container\n" "@param edge_pairs The edge pairs to insert\n" diff --git a/src/db/unit_tests/dbEdgePairs.cc b/src/db/unit_tests/dbEdgePairs.cc index edaccccdf..743899515 100644 --- a/src/db/unit_tests/dbEdgePairs.cc +++ b/src/db/unit_tests/dbEdgePairs.cc @@ -106,8 +106,9 @@ TEST(2) } struct EPTestFilter + : public db::EdgePairFilterBase { - bool operator() (const db::EdgePair &ep) + bool selected (const db::EdgePair &ep) const { return ep.first ().double_length () < 50; } diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index 4e2e5e3c6..7d49b9f37 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -1024,7 +1024,7 @@ void create_items_from_edge_pairs (rdb::Database *db, rdb::id_type cell_id, rdb: { typedef db::EdgePairs::const_iterator iter; - for (iter o = collection.begin (); o != collection.end (); ++o) { + for (iter o = collection.begin (); ! o.at_end (); ++o) { rdb::Item *item = db->create_item (cell_id, cat_id); item->values ().add (new rdb::Value (o->transformed (trans))); } From 255abc55340634d7b1b0077d8e9e3a854d75cf55 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 10 Nov 2018 00:44:30 +0100 Subject: [PATCH 048/335] Provided new explicit template instantiations required for DRC --- src/db/db/dbEdgePairs.cc | 2 ++ src/db/db/dbEdges.cc | 2 ++ src/db/db/dbRegion.cc | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index 7c0311fe7..6e0f5cbb7 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -100,6 +100,7 @@ void EdgePairs::insert (const db::Shape &shape, const T &trans) template void EdgePairs::insert (const db::Shape &, const db::ICplxTrans &); template void EdgePairs::insert (const db::Shape &, const db::Trans &); +template void EdgePairs::insert (const db::Shape &, const db::Disp &); void EdgePairs::clear () { @@ -121,6 +122,7 @@ EdgePairs &EdgePairs::transform (const T &trans) // explicit instantiations template EdgePairs &EdgePairs::transform (const db::ICplxTrans &); template EdgePairs &EdgePairs::transform (const db::Trans &); +template EdgePairs &EdgePairs::transform (const db::Disp &); const db::RecursiveShapeIterator & EdgePairs::iter () const diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index e4c8abbd8..f6b78eb3a 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -135,6 +135,7 @@ Edges &Edges::transform (const T &trans) // explicit instantiations template Edges &Edges::transform (const db::ICplxTrans &); template Edges &Edges::transform (const db::Trans &); +template Edges &Edges::transform (const db::Disp &); template void Edges::insert (const Sh &shape) @@ -161,6 +162,7 @@ void Edges::insert (const db::Shape &shape, const T &trans) template void Edges::insert (const db::Shape &, const db::ICplxTrans &); template void Edges::insert (const db::Shape &, const db::Trans &); +template void Edges::insert (const db::Shape &, const db::Disp &); FlatEdges * Edges::flat_edges () diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index b700600ff..74f8039c5 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -113,6 +113,7 @@ Region &Region::transform (const T &trans) // explicit instantiations template Region &Region::transform (const db::ICplxTrans &); template Region &Region::transform (const db::Trans &); +template Region &Region::transform (const db::Disp &); template void Region::insert (const Sh &shape) @@ -138,6 +139,7 @@ void Region::insert (const db::Shape &shape, const T &trans) template void Region::insert (const db::Shape &, const db::ICplxTrans &); template void Region::insert (const db::Shape &, const db::Trans &); +template void Region::insert (const db::Shape &, const db::Disp &); FlatRegion * Region::flat_region () From c91f54569aecec02d23c544394d7138f024cc49c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 10 Nov 2018 22:41:36 +0100 Subject: [PATCH 049/335] Provide GSI bindings for the new edge pair support This affects Shapes and Shape. New methods from EdgePairs are added and minor enhancements of Region and Edges. Ruby tests added --- src/db/db/db.pro | 6 +- src/db/db/dbAsIfFlatEdges.cc | 2 +- src/db/db/dbDeepShapeStore.cc | 24 +++++ src/db/db/dbDeepShapeStore.h | 122 +++++++++++++++++++++++++ src/db/db/dbEdgePairs.h | 14 ++- src/db/db/dbEdges.h | 13 ++- src/db/db/dbFlatEdgePairs.cc | 1 + src/db/db/dbFlatEdges.cc | 5 + src/db/db/dbOriginalLayerEdgePairs.cc | 3 +- src/db/db/dbOriginalLayerEdges.cc | 2 +- src/db/db/dbOriginalLayerRegion.cc | 2 +- src/db/db/dbRegion.h | 14 ++- src/db/db/gsiDeclDbEdgePairs.cc | 108 +++++++++++++++++++++- src/db/db/gsiDeclDbEdges.cc | 21 ++++- src/db/db/gsiDeclDbRegion.cc | 23 ++++- src/db/db/gsiDeclDbShape.cc | 62 ++++++++++++- src/db/db/gsiDeclDbShapes.cc | 73 ++++++++++++--- src/db/unit_tests/dbTilingProcessor.cc | 1 + testdata/ruby/dbEdgePairsTest.rb | 45 +++++++++ testdata/ruby/dbEdgesTest.rb | 17 +++- testdata/ruby/dbRegionTest.rb | 8 +- testdata/ruby/dbShapesTest.rb | 96 +++++++++++++++++++ 22 files changed, 625 insertions(+), 37 deletions(-) create mode 100644 src/db/db/dbDeepShapeStore.cc create mode 100644 src/db/db/dbDeepShapeStore.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 62e31b56a..ff156a235 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -135,7 +135,8 @@ SOURCES = \ dbEmptyEdgePairs.cc \ dbFlatEdgePairs.cc \ dbOriginalLayerEdgePairs.cc \ - dbEdgePairsDelegate.cc + dbEdgePairsDelegate.cc \ + dbDeepShapeStore.cc HEADERS = \ dbArray.h \ @@ -240,7 +241,8 @@ HEADERS = \ dbEmptyEdgePairs.h \ dbFlatEdgePairs.h \ dbOriginalLayerEdgePairs.h \ - dbEdgePairsDelegate.h + dbEdgePairsDelegate.h \ + dbDeepShapeStore.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index a97bb9fa9..2b709189a 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -781,7 +781,7 @@ EdgesDelegate * AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const { std::auto_ptr output (new FlatEdges (true)); - EdgeBooleanClusterCollector cluster_collector (output.get (), op); + EdgeBooleanClusterCollector cluster_collector (&output->raw_edges (), op); db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size () + (other ? other->size () : 0)); diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc new file mode 100644 index 000000000..e51bd5a3a --- /dev/null +++ b/src/db/db/dbDeepShapeStore.cc @@ -0,0 +1,24 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 + +*/ + + +// ... diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h new file mode 100644 index 000000000..2a32c2439 --- /dev/null +++ b/src/db/db/dbDeepShapeStore.h @@ -0,0 +1,122 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbDeepShapeStore +#define HDR_dbDeepShapeStore + +#include "dbCommon.h" + +#include "tlObject.h" +#include "dbLayout.h" +#include "dbRecursiveShapeIterator.h" + +namespace db { + +class DeepShapeStore; + +/** + * @brief Represents a shape collection from the deep shape store + * + * This is a lightweight class pointing into the deep shape store. + * DeepLayer objects are issued by the DeepShapeStore class. + */ +class DB_PUBLIC DeepLayer +{ +public: + /** + * @brief Destructor + */ + ~DeepLayer (); + + /** + * @brief Copy constructor + */ + DeepLayer (const DeepLayer &other); + + /** + * @brief Assignment + */ + DeepLayer &operator= (const DeepLayer &other); + + /** + * @brief Gets the layout object + * + * The return value is guaranteed to be non-null. + */ + db::Layout *layout (); + + /** + * @brief Gets the layer + */ + unsigned int layer () const + { + return m_layer; + } + +private: + friend class DeepShapeStore; + + /** + * @brief The constructor + */ + DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer); + + unsigned int layout () const { return m_layout; } + unsigned int layer () const { return m_layer; } + + tl::weak_ptr mp_store; + unsigned int m_layout; + unsigned int m_layer; +}; + +/** + * @brief The "deep shape store" is a working model for the hierarchical ("deep") processor + * + * The deep shape store keep temporary data for the deep shape processor. + * It mainly consists of layout objects holding the hierarchy trees and layers + * for the actual shapes. + * + * The deep shape store provides the basis for working with deep regions. On preparation, + * shapes are copied into the deep shape store. After fininishing, the shapes are copied + * back into the original layout. The deep shape store provides the methods and + * algorithms for doing the preparation and transfer. + */ +class DB_PUBLIC DeepShapeStore + : public tl::Object +{ +public: + /** + * @brief The default constructor + */ + DeepShapeStore (); + +private: + // no copying + DeepShapeStore (const DeepShapeStore &); + DeepShapeStore &operator= (const DeepShapeStore &); +}; + +} + +#endif + diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 55b14bd8b..93b9efa1a 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -493,14 +493,24 @@ public: /** * @brief Returns the nth edge pair * - * This operation is only cheap if "has_valid_edge_pairs" is true. Otherwise, the - * complexity is O(n). + * This operation is available only for flat regions - i.e. such for which + * "has_valid_edge_pairs" is true. */ const db::EdgePair *nth (size_t n) const { return mp_delegate->nth (n); } + /** + * @brief Forces flattening of the edge pair collection + * + * This method will turn any edge pair collection into a flat one. + */ + void flatten () + { + flat_edge_pairs (); + } + /** * @brief Returns true, if the edge pair set has valid edges stored within itself * diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index d3d6f9333..df85cdac9 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -1172,14 +1172,23 @@ public: /** * @brief Returns the nth edge * - * This operation is only cheap if "has_valid_edges" is true. Otherwise, the - * complexity is O(n). + * This operation is available only for flat regions - i.e. such for which "has_valid_edges" is true. */ const db::Edge *nth (size_t n) const { return mp_delegate->nth (n); } + /** + * @brief Forces flattening of the edge collection + * + * This method will turn any edge collection into a flat one. + */ + void flatten () + { + flat_edges (); + } + /** * @brief Returns true, if the edge set has valid edges stored within itself * diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc index 2db48f554..9c4c66747 100644 --- a/src/db/db/dbFlatEdgePairs.cc +++ b/src/db/db/dbFlatEdgePairs.cc @@ -184,6 +184,7 @@ void FlatEdgePairs::insert (const db::EdgePair &ep) { m_edge_pairs.insert (ep); + invalidate_cache (); } void diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 4c33b6575..c1e3e4606 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -338,7 +338,12 @@ FlatEdges::insert (const db::SimplePolygon &polygon) void FlatEdges::insert (const db::Edge &edge) { + if (! empty ()) { + m_is_merged = false; + } + m_edges.insert (edge); + invalidate_cache (); } void diff --git a/src/db/db/dbOriginalLayerEdgePairs.cc b/src/db/db/dbOriginalLayerEdgePairs.cc index 14e92019a..6791e069e 100644 --- a/src/db/db/dbOriginalLayerEdgePairs.cc +++ b/src/db/db/dbOriginalLayerEdgePairs.cc @@ -23,6 +23,7 @@ #include "dbOriginalLayerEdgePairs.h" #include "dbEdgePairs.h" +#include "tlInternational.h" namespace db { @@ -150,7 +151,7 @@ OriginalLayerEdgePairs::empty () const const db::EdgePair * OriginalLayerEdgePairs::nth (size_t) const { - tl_assert (false); + throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections"))); } bool diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc index 5b33e4f02..52e00afa7 100644 --- a/src/db/db/dbOriginalLayerEdges.cc +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -195,7 +195,7 @@ OriginalLayerEdges::is_merged () const const db::Edge * OriginalLayerEdges::nth (size_t) const { - tl_assert (false); + throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections"))); } bool diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc index a043ab411..b1bd13585 100644 --- a/src/db/db/dbOriginalLayerRegion.cc +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -195,7 +195,7 @@ OriginalLayerRegion::is_merged () const const db::Polygon * OriginalLayerRegion::nth (size_t) const { - tl_assert (false); + throw tl::Exception (tl::to_string (tr ("Random access to polygons is available only for flat regions"))); } bool diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 3ef7d0223..c0772dc89 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1576,14 +1576,24 @@ public: /** * @brief Returns the nth polygon * - * This operation is only cheap if "has_valid_polygons" is true. Otherwise, the - * complexity is O(n). + * This operation is available only for flat regions - i.e. such for which + * "has_valid_polygons" is true. */ const db::Polygon *nth (size_t n) const { return mp_delegate->nth (n); } + /** + * @brief Forces flattening of the region + * + * This method will turn any region into a flat shape collection. + */ + void flatten () + { + flat_region (); + } + /** * @brief Returns true, if the region has valid polygons stored within itself * diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 03df7340b..b8277d2e3 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -35,6 +35,35 @@ static db::EdgePairs *new_v () return new db::EdgePairs (); } +static db::EdgePairs *new_a (const std::vector &pairs) +{ + return new db::EdgePairs (pairs.begin (), pairs.end ()); +} + +static db::EdgePairs *new_ep (const db::EdgePair &pair) +{ + return new db::EdgePairs (pair); +} + +static db::EdgePairs *new_shapes (const db::Shapes &s) +{ + db::EdgePairs *r = new db::EdgePairs (); + for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::EdgePairs); !i.at_end (); ++i) { + r->insert (*i); + } + return r; +} + +static db::EdgePairs *new_si (const db::RecursiveShapeIterator &si) +{ + return new db::EdgePairs (si); +} + +static db::EdgePairs *new_si2 (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) +{ + return new db::EdgePairs (si, trans); +} + static std::string to_string0 (const db::EdgePairs *r) { return r->to_string (); @@ -135,6 +164,67 @@ Class decl_EdgePairs ("db", "EdgePairs", "\n" "This constructor creates an empty edge pair collection.\n" ) + + constructor ("new", &new_a, + "@brief Constructor from an edge pair array\n" + "@args array\n" + "\n" + "This constructor creates an edge pair collection from an array of \\EdgePair objects.\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + constructor ("new", &new_ep, + "@brief Constructor from a single edge pair object\n" + "@args edge_pair\n" + "\n" + "This constructor creates an edge pair collection with a single edge pair.\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + constructor ("new", &new_shapes, + "@brief Shapes constructor\n" + "@args shapes\n" + "\n" + "This constructor creates an edge pair collection from a \\Shapes collection.\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + constructor ("new", &new_si, + "@brief Constructor from a hierarchical shape set\n" + "@args shape_iterator\n" + "\n" + "This constructor creates an edge pair collection from the shapes delivered by the given recursive shape iterator.\n" + "Only edge pairs are taken from the shape set and other shapes are ignored.\n" + "This method allows feeding the edge pair collection from a hierarchy of cells.\n" + "\n" + "@code\n" + "layout = ... # a layout\n" + "cell = ... # the index of the initial cell\n" + "layer = ... # the index of the layer from where to take the shapes from\n" + "r = RBA::EdgePairs::new(layout.begin_shapes(cell, layer))\n" + "@/code\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + constructor ("new", &new_si2, + "@brief Constructor from a hierarchical shape set with a transformation\n" + "@args shape_iterator, trans\n" + "\n" + "This constructor creates an edge pair collection from the shapes delivered by the given recursive shape iterator.\n" + "Only edge pairs are taken from the shape set and other shapes are ignored.\n" + "The given transformation is applied to each edge pair taken.\n" + "This method allows feeding the edge pair collection from a hierarchy of cells.\n" + "The transformation is useful to scale to a specific database unit for example.\n" + "\n" + "@code\n" + "layout = ... # a layout\n" + "cell = ... # the index of the initial cell\n" + "layer = ... # the index of the layer from where to take the shapes from\n" + "dbu = 0.1 # the target database unit\n" + "r = RBA::EdgePairs::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" + "@/code\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + method ("insert", (void (db::EdgePairs::*) (const db::Edge &, const db::Edge &)) &db::EdgePairs::insert, "@brief Inserts an edge pair into the collection\n" "@args first, second\n" @@ -337,7 +427,23 @@ Class decl_EdgePairs ("db", "EdgePairs", "@brief Returns the nth edge pair\n" "@args n\n" "\n" - "This method returns nil if the index is out of range.\n" + "This method returns nil if the index is out of range. It is available for flat edge pairs only - i.e. " + "those for which \\has_valid_edge_pairs? is true. Use \\flatten to explicitly flatten an edge pair collection.\n" + "\n" + "The \\each iterator is the more general approach to access the edge pairs." + ) + + method ("flatten", &db::EdgePairs::flatten, + "@brief Explicitly flattens an edge pair collection\n" + "\n" + "If the collection is already flat (i.e. \\has_valid_edge_pairs? returns true), this method will " + "not change the collection.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + method ("has_valid_edge_pairs?", &db::EdgePairs::has_valid_edge_pairs, + "@brief Returns true if the edge pair collection is flat and individual edge pairs can be accessed randomly\n" + "\n" + "This method has been introduced in version 0.26." ) + method ("enable_progress", &db::EdgePairs::enable_progress, "@brief Enable progress reporting\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 85b22a2f8..9166a6f68 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -1433,10 +1433,27 @@ Class dec_Edges ("db", "Edges", "This method has been introduced in version 0.25." ) + method ("[]", &db::Edges::nth, - "@brief Returns the nth edge of the edge collection\n" + "@brief Returns the nth edge of the collection\n" "@args n\n" "\n" - "This method returns nil if the index is out of range.\n" + "This method returns nil if the index is out of range. It is available for flat edge collections only - i.e. " + "those for which \\has_valid_edges? is true. Use \\flatten to explicitly flatten an edge collection.\n" + "This method returns the raw edge (not merged edges, even if merged semantics is enabled).\n" + "\n" + "The \\each iterator is the more general approach to access the edges." + ) + + method ("flatten", &db::Edges::flatten, + "@brief Explicitly flattens an edge collection\n" + "\n" + "If the collection is already flat (i.e. \\has_valid_edges? returns true), this method will " + "not change it.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + method ("has_valid_edges?", &db::Edges::has_valid_edges, + "@brief Returns true if the edge collection is flat and individual edges can be accessed randomly\n" + "\n" + "This method has been introduced in version 0.26." ) + method_ext ("to_s", &to_string0, "@brief Converts the edge collection to a string\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 6bb770ebb..7cb106fa8 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -797,7 +797,7 @@ Class decl_Region ("db", "Region", "@param as_pattern If true, the selection string is treated as a glob pattern. Otherwise the match is exact.\n" "\n" "This special constructor will create a region from the text objects delivered by the shape iterator. " - "Each text object will deliver a small (non-empty) box that represents the text origin.\n" + "Each text object will give a small (non-empty) box that represents the text origin.\n" "Texts can be selected by their strings - either through a glob pattern or by exact comparison with " "the given string. The following options are available:\n" "\n" @@ -2320,11 +2320,24 @@ Class decl_Region ("db", "Region", "@brief Returns the nth polygon of the region\n" "@args n\n" "\n" - "This method returns nil if the index is out of range.\n" - "This returns the raw polygon (not merged polygons if merged semantics is enabled).\n" + "This method returns nil if the index is out of range. It is available for flat regions only - i.e. " + "those for which \\has_valid_polygons? is true. Use \\flatten to explicitly flatten a region.\n" + "This method returns the raw polygon (not merged polygons, even if merged semantics is enabled).\n" "\n" - "Using this method may be costly in terms of memory since it will load the polygons into an array if they have been " - "stored in an hierarchical layout before. It is recommended to use the \\each iterator instead if possible." + "The \\each iterator is the more general approach to access the polygons." + ) + + method ("flatten", &db::Region::flatten, + "@brief Explicitly flattens a region\n" + "\n" + "If the region is already flat (i.e. \\has_valid_polygons? returns true), this method will " + "not change it.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + method ("has_valid_polygons?", &db::Region::has_valid_polygons, + "@brief Returns true if the region is flat and individual polygons can be accessed randomly\n" + "\n" + "This method has been introduced in version 0.26." ) + method_ext ("to_s", &to_string0, "@brief Converts the region to a string\n" diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc index 309adeae2..35aa60cff 100644 --- a/src/db/db/gsiDeclDbShape.cc +++ b/src/db/db/gsiDeclDbShape.cc @@ -689,6 +689,26 @@ static tl::Variant get_dedge (const db::Shape *s) } } +static tl::Variant get_edge_pair (const db::Shape *s) +{ + db::Shape::edge_pair_type p; + if (s->edge_pair (p)) { + return tl::Variant (p); + } else { + return tl::Variant (); + } +} + +static tl::Variant get_dedge_pair (const db::Shape *s) +{ + db::Shape::edge_pair_type p; + if (s->edge_pair (p)) { + return tl::Variant (db::CplxTrans (shape_dbu (s)) * p); + } else { + return tl::Variant (); + } +} + static tl::Variant get_text (const db::Shape *s) { db::Shape::text_type p; @@ -1087,6 +1107,7 @@ static int t_simplePolygonRef () { return db::Shape::SimplePolygonRef static int t_simplePolygonPtrArray () { return db::Shape::SimplePolygonPtrArray; } static int t_simplePolygonPtrArrayMember () { return db::Shape::SimplePolygonPtrArrayMember; } static int t_edge () { return db::Shape::Edge; } +static int t_edge_pair () { return db::Shape::EdgePair; } static int t_path () { return db::Shape::Path; } static int t_pathRef () { return db::Shape::PathRef; } static int t_pathPtrArray () { return db::Shape::PathPtrArray; } @@ -1218,7 +1239,7 @@ Class decl_Shape ("db", "Shape", "\n" "This method has been introduced in version 0.25." ) + - gsi::method_ext ("edge=", &set_shape, + gsi::method_ext ("edge=", &set_shape, gsi::arg("edge"), "@brief Replaces the shape by the given edge\n" "@args box\n" "This method replaces the shape by the given edge. This method can only be called " @@ -1235,6 +1256,23 @@ Class decl_Shape ("db", "Shape", "\n" "This method has been introduced in version 0.25." ) + + gsi::method_ext ("edge_pair=", &set_shape, gsi::arg("edge_pair"), + "@brief Replaces the shape by the given edge pair\n" + "@args box\n" + "This method replaces the shape by the given edge pair. This method can only be called " + "for editable layouts. It does not change the user properties of the shape.\n" + "Calling this method will invalidate any iterators. It should not be called inside a " + "loop iterating over shapes.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + gsi::method_ext ("edge_pair=|dedge_pair=", &set_dshape, gsi::arg("edge_pair"), + "@brief Replaces the shape by the given edge pair (in micrometer units)\n" + "This method replaces the shape by the given edge pair, like \\edge_pair= with a \\EdgePair argument does. " + "This version translates the edge pair from micrometer units to database units internally.\n" + "\n" + "This method has been introduced in version 0.26." + ) + gsi::method_ext ("delete_property", &delete_property, "@brief Deletes the user property with the given key\n" "@args key\n" @@ -1713,12 +1751,29 @@ Class decl_Shape ("db", "Shape", "Starting with version 0.23, this method returns nil, if the shape does not represent an edge." ) + gsi::method_ext ("dedge", &get_dedge, - "@brief Returns the path object as a \\DEdge object in micrometer units\n" + "@brief Returns the edge object as a \\DEdge object in micrometer units\n" "See \\edge for a description of this method. This method returns the edge after translation to " "micrometer units.\n" "\n" "This method has been added in version 0.25.\n" ) + + gsi::method ("is_edge_pair?", &db::Shape::is_edge_pair, + "@brief Returns true, if the object is an edge pair\n" + "\n" + "This method has been introduced in version 0.26." + ) + + gsi::method_ext ("edge_pair", &get_edge_pair, + "@brief Returns the edge pair object\n" + "\n" + "This method has been introduced in version 0.26." + ) + + gsi::method_ext ("dedge_pair", &get_dedge_pair, + "@brief Returns the edge pair object as a \\DEdgePair object in micrometer units\n" + "See \\edge_pair for a description of this method. This method returns the edge pair after translation to " + "micrometer units.\n" + "\n" + "This method has been added in version 0.26.\n" + ) + gsi::method ("is_text?", &db::Shape::is_text, "@brief Returns true, if the object is a text\n" ) + @@ -1729,7 +1784,7 @@ Class decl_Shape ("db", "Shape", ) + gsi::method_ext ("dtext", &get_dtext, "@brief Returns the path object as a \\DText object in micrometer units\n" - "See \\edge for a description of this method. This method returns the text after translation to " + "See \\text for a description of this method. This method returns the text after translation to " "micrometer units.\n" "\n" "This method has been added in version 0.25.\n" @@ -2026,6 +2081,7 @@ Class decl_Shape ("db", "Shape", gsi::method ("TSimplePolygonPtrArray|#t_simple_polygon_ptr_array", &t_simplePolygonPtrArray) + gsi::method ("TSimplePolygonPtrArrayMember|#t_simple_polygon_ptr_array_member", &t_simplePolygonPtrArrayMember) + gsi::method ("TEdge|#t_edge", &t_edge) + + gsi::method ("TEdgePair|#t_edge_pair", &t_edge_pair) + gsi::method ("TPath|#t_path", &t_path) + gsi::method ("TPathRef|#t_path_ref", &t_pathRef) + gsi::method ("TPathPtrArray|#t_path_ptr_array", &t_pathPtrArray) + diff --git a/src/db/db/gsiDeclDbShapes.cc b/src/db/db/gsiDeclDbShapes.cc index 24291e3cf..27653f9b3 100644 --- a/src/db/db/gsiDeclDbShapes.cc +++ b/src/db/db/gsiDeclDbShapes.cc @@ -423,6 +423,7 @@ static unsigned int s_properties () { return db::ShapeIterator::Propert static unsigned int s_polygons () { return db::ShapeIterator::Polygons; } static unsigned int s_boxes () { return db::ShapeIterator::Boxes; } static unsigned int s_edges () { return db::ShapeIterator::Edges; } +static unsigned int s_edge_pairs () { return db::ShapeIterator::EdgePairs; } static unsigned int s_paths () { return db::ShapeIterator::Paths; } static unsigned int s_texts () { return db::ShapeIterator::Texts; } static unsigned int s_user_objects () { return db::ShapeIterator::UserObjects; } @@ -780,9 +781,8 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + - gsi::method_ext ("replace", &replace, + gsi::method_ext ("replace", &replace, gsi::arg ("shape"), gsi::arg ("edge"), "@brief Replaces the given shape with an edge object\n" - "@args shape,edge\n" "\n" "This method has been introduced with version 0.16. It replaces the given shape with the " "object specified. It does not change the property Id. To change the property Id, " @@ -792,7 +792,7 @@ Class decl_Shapes ("db", "Shapes", "This method is permitted in editable mode only." ) + gsi::method_ext ("replace", &dreplace, gsi::arg ("shape"), gsi::arg ("edge"), - "@brief Replaces the given shape with a edge given in micrometer units\n" + "@brief Replaces the given shape with an edge given in micrometer units\n" "@return A reference to the new shape (a \\Shape object)\n" "\n" "This method behaves like the \\replace version with an \\Edge argument, except that it will " @@ -800,6 +800,27 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + + gsi::method_ext ("replace", &replace, gsi::arg ("shape"), gsi::arg ("edge_pair"), + "@brief Replaces the given shape with an edge pair object\n" + "\n" + "It replaces the given shape with the " + "object specified. It does not change the property Id. To change the property Id, " + "use the \\replace_prop_id method. To replace a shape and discard the property Id, erase the " + "shape and insert a new shape." + "\n" + "This method is permitted in editable mode only.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("replace", &dreplace, gsi::arg ("shape"), gsi::arg ("edge_pair"), + "@brief Replaces the given shape with an edge pair given in micrometer units\n" + "@return A reference to the new shape (a \\Shape object)\n" + "\n" + "This method behaves like the \\replace version with an \\EdgePair argument, except that it will " + "internally translate the edge pair from micrometer to database units.\n" + "\n" + "This variant has been introduced in version 0.26.\n" + ) + gsi::method_ext ("replace", &replace, "@brief Replaces the given shape with a text object\n" "@return A reference to the new shape (a \\Shape object)\n" @@ -893,9 +914,8 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + - gsi::method_ext ("insert|#insert_edge", &insert, - "@brief Inserts a edge into the shapes list\n" - "@args edge\n" + gsi::method_ext ("insert|#insert_edge", &insert, gsi::arg ("edge"), + "@brief Inserts an edge into the shapes list\n" "\n" "Starting with version 0.16, this method returns a reference to the newly created shape\n" ) + @@ -907,6 +927,19 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + + gsi::method_ext ("insert", &insert, gsi::arg ("edge_pair"), + "@brief Inserts an edge pair into the shapes list\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("insert", &dinsert, gsi::arg ("edge_pair"), + "@brief Inserts a micrometer-unit edge pair into the shapes list\n" + "@return A reference to the new shape (a \\Shape object)\n" + "This method behaves like the \\insert version with a \\EdgePair argument, except that it will " + "internally translate the edge pair from micrometer to database units.\n" + "\n" + "This variant has been introduced in version 0.26." + ) + gsi::method_ext ("insert|#insert_text", &insert, "@brief Inserts a text into the shapes list\n" "@return A reference to the new shape (a \\Shape object)\n" @@ -986,14 +1019,13 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + - gsi::method_ext ("insert|#insert_edge_with_properties", &insert_with_properties, - "@brief Inserts a edge with properties into the shapes list\n" - "@args edge, property_id\n" + gsi::method_ext ("insert|#insert_edge_with_properties", &insert_with_properties, gsi::arg ("edge"), gsi::arg ("property_id"), + "@brief Inserts an edge with properties into the shapes list\n" "@return A reference to the new shape (a \\Shape object)\n" "The property Id must be obtained from the \\Layout object's property_id method which " "associates a property set with a property Id." "\n" - "Starting with version 0.16, this method returns a reference to the newly created shape\n" + "Starting with version 0.16, this method returns a reference to the newly created shape.\n" ) + gsi::method_ext ("insert", &dinsert_with_properties, gsi::arg ("edge"), gsi::arg ("property_id"), "@brief Inserts a micrometer-unit edge with properties into the shapes list\n" @@ -1003,6 +1035,22 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + + gsi::method_ext ("insert", &insert_with_properties, gsi::arg ("edge_pair"), gsi::arg ("property_id"), + "@brief Inserts an edge pair with properties into the shapes list\n" + "@return A reference to the new shape (a \\Shape object)\n" + "The property Id must be obtained from the \\Layout object's property_id method which " + "associates a property set with a property Id." + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("insert", &dinsert_with_properties, gsi::arg ("edge_pair"), gsi::arg ("property_id"), + "@brief Inserts a micrometer-unit edge pair with properties into the shapes list\n" + "@return A reference to the new shape (a \\Shape object)\n" + "This method behaves like the \\insert version with a \\EdgePair argument and a property ID, except that it will " + "internally translate the edge pair from micrometer to database units.\n" + "\n" + "This variant has been introduced in version 0.26." + ) + gsi::method_ext ("insert|#insert_text_with_properties", &insert_with_properties, "@brief Inserts a text with properties into the shapes list\n" "@args text, property_id\n" @@ -1185,6 +1233,9 @@ Class decl_Shapes ("db", "Shapes", gsi::method ("SEdges|#s_edges", &s_edges, "@brief Indicates that edges shall be retrieved" ) + + gsi::method ("SEdgePairs|#s_edge_pairs", &s_edge_pairs, + "@brief Indicates that edge pairs shall be retrieved" + ) + gsi::method ("SPaths|#s_paths", &s_paths, "@brief Indicates that paths shall be retrieved" ) + @@ -1203,7 +1254,7 @@ Class decl_Shapes ("db", "Shapes", "@brief A collection of shapes\n" "\n" "A shapes collection is a collection of geometrical objects, such as " - "polygons, boxes, paths, edges or text objects.\n" + "polygons, boxes, paths, edges, edge pairs or text objects.\n" "\n" "Shapes objects are the basic containers for geometrical objects of a cell. Inside a cell, there is " "one Shapes object per layer.\n" diff --git a/src/db/unit_tests/dbTilingProcessor.cc b/src/db/unit_tests/dbTilingProcessor.cc index c352cb19a..516e5f613 100644 --- a/src/db/unit_tests/dbTilingProcessor.cc +++ b/src/db/unit_tests/dbTilingProcessor.cc @@ -238,6 +238,7 @@ TEST(3) tp.input ("i2", ir2.begin_iter ().first, ir2.begin_iter ().second); EXPECT_EQ (ir2.has_valid_polygons (), false); db::Region ir3 (db::RecursiveShapeIterator (ly, ly.cell (top), l3)); + ir3.flatten (); tp.input ("i3", ir3.begin_iter ().first, ir3.begin_iter ().second); EXPECT_EQ (ir3.has_valid_polygons (), true); tp.output ("o1", ly, top, o1); diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index b983a6f03..da529901b 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -108,6 +108,51 @@ class DBEdgePairs_TestClass < TestBase end + def test_3 + + ep1 = RBA::EdgePair::new(RBA::Edge::new(0, 1, 2, 3), RBA::Edge::new(10, 11, 12, 13)) + ep2 = RBA::EdgePair::new(RBA::Edge::new(20, 21, 22, 23), RBA::Edge::new(30, 31, 32, 33)) + + r1 = RBA::EdgePairs::new([ ep1, ep2 ]) + assert_equal(r1.to_s, "(0,1;2,3)/(10,11;12,13);(20,21;22,23)/(30,31;32,33)") + + r1 = RBA::EdgePairs::new(ep1) + assert_equal(r1.to_s, "(0,1;2,3)/(10,11;12,13)") + + s = RBA::Shapes::new + s.insert(ep1) + s.insert(ep2) + r1 = RBA::EdgePairs::new(s) + assert_equal(r1.to_s, "(0,1;2,3)/(10,11;12,13);(20,21;22,23)/(30,31;32,33)") + + ly = RBA::Layout::new + l1 = ly.layer("l1") + l2 = ly.layer("l2") + c1 = ly.create_cell("C1") + c2 = ly.create_cell("C2") + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(0, 0))) + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(0, 100))) + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(200, 100))) + c2.shapes(l1).insert(ep1) + c2.shapes(l2).insert(ep2) + + r = RBA::EdgePairs::new(ly.begin_shapes(c1.cell_index, l1)) + assert_equal(r.to_s(30), "(0,1;2,3)/(10,11;12,13);(0,101;2,103)/(10,111;12,113);(200,101;202,103)/(210,111;212,113)") + assert_equal(r.to_s(2), "(0,1;2,3)/(10,11;12,13);(0,101;2,103)/(10,111;12,113)...") + assert_equal(r.is_empty?, false) + assert_equal(r.size, 3) + + assert_equal(r.has_valid_edge_pairs?, false) + assert_equal(r.bbox.to_s, "(0,1;212,113)") + + r.flatten + assert_equal(r.has_valid_edge_pairs?, true) + assert_equal(r[1].to_s, "(0,101;2,103)/(10,111;12,113)") + assert_equal(r[100].inspect, "nil") + assert_equal(r.bbox.to_s, "(0,1;212,113)") + + end + end diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index b8a2e567c..4fea31683 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -40,6 +40,13 @@ class DBEdges_TestClass < TestBase assert_equal(r.is_empty?, false) assert_equal(r.size, 1) assert_equal(r.bbox.to_s, "(10,20;100,200)") + assert_equal(r.is_merged?, true) + + r.assign(RBA::Edges::new([RBA::Edge::new(10, 20, 100, 200), RBA::Edge::new(11, 21, 101, 201)])) + assert_equal(r.to_s, "(10,20;100,200);(11,21;101,201)") + assert_equal(r.is_empty?, false) + assert_equal(r.size, 2) + assert_equal(r.bbox.to_s, "(10,20;101,201)") assert_equal(r.is_merged?, false) r.assign(RBA::Edges::new(RBA::Edge::new(10, 20, 100, 200))) @@ -47,7 +54,7 @@ class DBEdges_TestClass < TestBase assert_equal(r.is_empty?, false) assert_equal(r.size, 1) assert_equal(r.bbox.to_s, "(10,20;100,200)") - assert_equal(r.is_merged?, false) + assert_equal(r.is_merged?, true) r.assign(RBA::Edges::new(RBA::Box::new(10, 20, 100, 200))) assert_equal(r.to_s, "(10,20;10,200);(10,200;100,200);(100,200;100,20);(100,20;10,20)") @@ -60,7 +67,7 @@ class DBEdges_TestClass < TestBase assert_equal(r.is_empty?, false) assert_equal(r.size, 4) assert_equal(r.bbox.to_s, "(10,20;100,200)") - assert_equal(r.is_merged?, false) + assert_equal(r.is_merged?, true) assert_equal(r.moved(RBA::Point::new(10, 20)).bbox.to_s, "(20,40;110,220)") assert_equal(r.moved(10, 20).bbox.to_s, "(20,40;110,220)") @@ -163,6 +170,12 @@ class DBEdges_TestClass < TestBase assert_equal(r.to_s(2), "(-10,-20;10,20);(-10,80;10,120)...") assert_equal(r.is_empty?, false) assert_equal(r.size, 3) + assert_equal(r.bbox.to_s, "(-10,-20;210,120)") + assert_equal(r.is_merged?, false) + assert_equal(r.has_valid_edges?, false) + + r.flatten + assert_equal(r.has_valid_edges?, true) assert_equal(r[1].to_s, "(-10,80;10,120)") assert_equal(r[100].to_s, "") assert_equal(r.bbox.to_s, "(-10,-20;210,120)") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index e9bdc6452..01d4ea556 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -41,7 +41,7 @@ class DBRegion_TestClass < TestBase assert_equal(r.is_empty?, false) assert_equal(r.size, 1) assert_equal(r.bbox.to_s, "(10,20;100,200)") - assert_equal(r.is_merged?, false) + assert_equal(r.is_merged?, true) assert_equal(r.is_box?, true) assert_equal(r.edges.to_s, "(10,20;10,200);(10,200;100,200);(100,200;100,20);(100,20;10,20)") @@ -149,6 +149,12 @@ class DBRegion_TestClass < TestBase assert_equal(r.to_s, "(-10,-20;-10,20;10,20;10,-20);(-10,80;-10,120;10,120;10,80);(190,80;190,120;210,120;210,80)") assert_equal(r.is_empty?, false) assert_equal(r.size, 3) + assert_equal(r.bbox.to_s, "(-10,-20;210,120)") + assert_equal(r.is_merged?, false) + assert_equal(r.has_valid_polygons?, false) + + r.flatten + assert_equal(r.has_valid_polygons?, true) assert_equal(r[1].to_s, "(-10,80;-10,120;10,120;10,80)") assert_equal(r[4].to_s, "") assert_equal(r.bbox.to_s, "(-10,-20;210,120)") diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index 00ce28bf6..27fbcd36c 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -155,6 +155,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].polygon.inspect, "(10,-10;10,40;50,40;50,-10)" ) assert_equal( arr[0].simple_polygon.inspect, "(10,-10;10,40;50,40;50,-10)" ) assert_equal( arr[0].edge.inspect, "nil" ) + assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "(10,-10;50,40)" ) assert_equal( arr[0].path.inspect, "nil" ) assert_equal( arr[0].text.inspect, "nil" ) @@ -178,6 +179,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].polygon.inspect, "nil" ) assert_equal( arr[0].simple_polygon.inspect, "nil" ) assert_equal( arr[0].edge.inspect, "(-1,2;5,2)" ) + assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "nil" ) assert_equal( arr[0].path.inspect, "nil" ) assert_equal( arr[0].text.inspect, "nil" ) @@ -188,6 +190,32 @@ class DBShapes_TestClass < TestBase assert_equal( arr.size, 1 ) assert_equal( arr[0].is_box?, true ) + # edge pairs + + a = RBA::EdgePair::new(RBA::Edge::new(RBA::Point::new(-1, 2), RBA::Point::new(5, 2)), RBA::Edge::new(RBA::Point::new(-1, 5), RBA::Point::new(5, 5))) + c1.shapes( lindex ).insert( a ) + arr = [] + shapes.each( RBA::Shapes::SEdgePairs ) { |s| arr.push( s ) } + assert_equal( arr.size, 1 ) + assert_equal( arr[0].prop_id, 0 ) + assert_equal( arr[0].has_prop_id?, false ) + assert_equal( arr[0].is_null?, false ) + assert_equal( arr[0].type, RBA::Shape::t_edge_pair ) + assert_equal( arr[0].is_edge_pair?, true ) + assert_equal( arr[0].polygon.inspect, "nil" ) + assert_equal( arr[0].simple_polygon.inspect, "nil" ) + assert_equal( arr[0].edge_pair.inspect, "(-1,2;5,2)/(-1,5;5,5)" ) + assert_equal( arr[0].edge.inspect, "nil" ) + assert_equal( arr[0].box.inspect, "nil" ) + assert_equal( arr[0].path.inspect, "nil" ) + assert_equal( arr[0].text.inspect, "nil" ) + assert_equal( arr[0].edge_pair == a, true ) + assert_equal( arr[0].bbox == a.bbox, true ) + arr = [] + shapes.each( RBA::Shapes::SBoxes ) { |s| arr.push( s ) } + assert_equal( arr.size, 1 ) + assert_equal( arr[0].is_box?, true ) + # paths a = RBA::Path::new( [ RBA::Point::new( 0, 10 ), RBA::Point::new( 10, 50 ) ], 25 ) @@ -203,6 +231,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].polygon.inspect, "(12,7;-12,13;-2,53;22,47)" ) assert_equal( arr[0].simple_polygon.inspect, "(12,7;-12,13;-2,53;22,47)" ) assert_equal( arr[0].edge.inspect, "nil" ) + assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "nil" ) assert_equal( arr[0].path.inspect, "(0,10;10,50) w=25 bx=0 ex=0 r=false" ) assert_equal( arr[0].text.inspect, "nil" ) @@ -238,6 +267,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].polygon.inspect, "(0,1;1,5;5,5)" ) assert_equal( arr[0].simple_polygon.inspect, "(0,1;1,5;5,5)" ) assert_equal( arr[0].edge.inspect, "nil" ) + assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "nil" ) assert_equal( arr[0].path.inspect, "nil" ) assert_equal( arr[0].text.inspect, "nil" ) @@ -419,6 +449,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dpolygon.inspect, "(0.01,-0.01;0.01,0.04;0.05,0.04;0.05,-0.01)" ) assert_equal( arr[0].dsimple_polygon.inspect, "(0.01,-0.01;0.01,0.04;0.05,0.04;0.05,-0.01)" ) assert_equal( arr[0].dedge.inspect, "nil" ) + assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "(0.01,-0.01;0.05,0.04)" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) @@ -442,6 +473,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dpolygon.inspect, "nil" ) assert_equal( arr[0].dsimple_polygon.inspect, "nil" ) assert_equal( arr[0].dedge.inspect, "(-0.001,0.002;0.005,0.002)" ) + assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "nil" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) @@ -451,6 +483,31 @@ class DBShapes_TestClass < TestBase assert_equal( arr.size, 1 ) assert_equal( arr[0].is_box?, true ) + # edge pairs + + a = RBA::DEdgePair::new(RBA::DEdge::new(RBA::DPoint::new(-0.001, 0.002), RBA::DPoint::new(0.005, 0.002)), RBA::DEdge::new(RBA::DPoint::new(-0.001, 0.005), RBA::DPoint::new(0.005, 0.005))) + c1.shapes( lindex ).insert( a ) + arr = [] + shapes.each( RBA::Shapes::SEdgePairs ) { |s| arr.push( s ) } + assert_equal( arr.size, 1 ) + assert_equal( arr[0].prop_id, 0 ) + assert_equal( arr[0].has_prop_id?, false ) + assert_equal( arr[0].is_null?, false ) + assert_equal( arr[0].type, RBA::Shape::t_edge_pair ) + assert_equal( arr[0].is_edge_pair?, true ) + assert_equal( arr[0].dpolygon.inspect, "nil" ) + assert_equal( arr[0].dsimple_polygon.inspect, "nil" ) + assert_equal( arr[0].dedge_pair.inspect, "(-0.001,0.002;0.005,0.002)/(-0.001,0.005;0.005,0.005)" ) + assert_equal( arr[0].dedge.inspect, "nil" ) + assert_equal( arr[0].dbox.inspect, "nil" ) + assert_equal( arr[0].dpath.inspect, "nil" ) + assert_equal( arr[0].dtext.inspect, "nil" ) + assert_equal( arr[0].dbbox.inspect, "(-0.001,0.002;0.005,0.005)" ) + arr = [] + shapes.each( RBA::Shapes::SBoxes ) { |s| arr.push( s ) } + assert_equal( arr.size, 1 ) + assert_equal( arr[0].is_box?, true ) + # paths a = RBA::DPath::new( [ RBA::DPoint::new( 0, 0.010 ), RBA::DPoint::new( 0.010, 0.050 ) ], 0.025 ) @@ -466,6 +523,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dpolygon.inspect, "(0.012,0.007;-0.012,0.013;-0.002,0.053;0.022,0.047)" ) assert_equal( arr[0].dsimple_polygon.inspect, "(0.012,0.007;-0.012,0.013;-0.002,0.053;0.022,0.047)" ) assert_equal( arr[0].dedge.inspect, "nil" ) + assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "nil" ) assert_equal( arr[0].dpath.inspect, "(0,0.01;0.01,0.05) w=0.025 bx=0 ex=0 r=false" ) assert_equal( arr[0].dtext.inspect, "nil" ) @@ -499,6 +557,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dpolygon.inspect, "(0,0.001;0.001,0.005;0.005,0.005)" ) assert_equal( arr[0].dsimple_polygon.inspect, "(0,0.001;0.001,0.005;0.005,0.005)" ) assert_equal( arr[0].dedge.inspect, "nil" ) + assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "nil" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) @@ -720,6 +779,18 @@ class DBShapes_TestClass < TestBase shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } assert_equal( arr, ["simple_polygon (14,0;-21,35;7,64;42,28)", "box (10,-10;50,40) prop_id=17"] ) + s2 = shapes.replace( s2, RBA::Edge::new( 10, -10, 50, 40 ) ) + + arr = [] + shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["simple_polygon (14,0;-21,35;7,64;42,28)", "edge (10,-10;50,40) prop_id=17"] ) + + s2 = shapes.replace( s2, RBA::EdgePair::new( RBA::Edge::new( 10, -10, 50, 40 ), RBA::Edge::new( 10, 0, 50, 30 ) ) ) + + arr = [] + shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["simple_polygon (14,0;-21,35;7,64;42,28)", "edge_pair (10,-10;50,40)/(10,0;50,30) prop_id=17"] ) + shapes.erase( s2 ) arr = [] @@ -775,6 +846,7 @@ class DBShapes_TestClass < TestBase assert_equal(s2.simple_polygon.inspect, "nil") assert_equal(s2.text.inspect, "('text',r0 100,200)") assert_equal(s2.edge.inspect, "nil") + assert_equal(s2.edge_pair.inspect, "nil") assert_equal(s2.path.inspect, "nil") assert_equal(s2.box.inspect, "nil") @@ -784,6 +856,18 @@ class DBShapes_TestClass < TestBase shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } assert_equal( arr, ["box (11,-11;51,41)", "box (200,-10;250,40)", "text ('text',r0 100,200)"] ) + s3.edge_pair = RBA::EdgePair::new(RBA::Edge::new(RBA::Point::new(1, 2), RBA::Point::new(3, 4)), RBA::Edge::new(RBA::Point::new(1, 12), RBA::Point::new(3, 14))) + + shapes = c1.shapes( lindex ) + + arr = [] + shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["edge_pair (1,2;3,4)/(1,12;3,14)", "box (11,-11;51,41)", "text ('text',r0 100,200)"] ) + + arr = [] + shapes.each( RBA::Shapes::SEdgePairs ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["edge_pair (1,2;3,4)/(1,12;3,14)"] ) + s3.edge = RBA::Edge::new( RBA::Point::new( 1, 2 ), RBA::Point::new( 3, 4 ) ) shapes = c1.shapes( lindex ) @@ -792,6 +876,10 @@ class DBShapes_TestClass < TestBase shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } assert_equal( arr, ["edge (1,2;3,4)", "box (11,-11;51,41)", "text ('text',r0 100,200)"] ) + arr = [] + shapes.each( RBA::Shapes::SEdges ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["edge (1,2;3,4)"] ) + pts = [ RBA::Point::new( 100, 200 ), RBA::Point::new( 400, 300 ), RBA::Point::new( 500, 600 ) ] s1.polygon = RBA::Polygon::new( pts ) @@ -990,6 +1078,14 @@ class DBShapes_TestClass < TestBase shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } assert_equal( arr, ["box (11,-11;51,41)", "box (200,-10;250,40)", "text ('text',r0 100,200)"] ) + s3.edge_pair = RBA::DEdgePair::new(RBA::DEdge::new(RBA::DPoint::new(0.001, 0.002), RBA::DPoint::new(0.003, 0.004)), RBA::DEdge::new(RBA::DPoint::new(0.001, 0.012), RBA::DPoint::new(0.003, 0.014))) + + shapes = c1.shapes( lindex ) + + arr = [] + shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["edge_pair (1,2;3,4)/(1,12;3,14)", "box (11,-11;51,41)", "text ('text',r0 100,200)"] ) + s3.edge = RBA::DEdge::new( RBA::DPoint::new( 0.001, 0.002 ), RBA::DPoint::new( 0.003, 0.004 ) ) shapes = c1.shapes( lindex ) From f346e70746435ddd675f5cd6c38d768889779e08 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 12 Nov 2018 23:44:26 +0100 Subject: [PATCH 050/335] RecursiveShapeIterator now features a push mode This will eventually enable catching a hierarchy skeleton (with shapes) from a RecursiveShapeIterator. --- src/db/db/dbRecursiveShapeIterator.cc | 101 ++++- src/db/db/dbRecursiveShapeIterator.h | 149 +++++++- src/db/db/gsiDeclDbRecursiveShapeIterator.cc | 2 +- src/db/unit_tests/dbRecursiveShapeIterator.cc | 351 ++++++++++++++++++ 4 files changed, 564 insertions(+), 39 deletions(-) diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index b41c243ac..9942a0c2c 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -383,7 +383,7 @@ private: } void -RecursiveShapeIterator::validate () const +RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const { if (! m_needs_reinit) { return; @@ -438,8 +438,8 @@ RecursiveShapeIterator::validate () const } else if (mp_layout && (! m_has_layers || m_current_layer < m_layers.size ())) { // Ensures the trees are built properly - this is important in MT contexts (i.e. TilingProcessor) mp_layout->update (); - new_cell (); - next_shape (); + new_cell (receiver); + next_shape (receiver); } } @@ -519,7 +519,7 @@ RecursiveShapeIterator::select_all_cells () bool RecursiveShapeIterator::at_end () const { - validate (); + validate (0); return m_shape.at_end () || is_inactive (); } @@ -611,7 +611,7 @@ RecursiveShapeIterator::skip_inst_iter_for_complex_region () const } void -RecursiveShapeIterator::next () +RecursiveShapeIterator::next (RecursiveShapeReceiver *receiver) { if (! at_end ()) { @@ -622,14 +622,14 @@ RecursiveShapeIterator::next () } if (! mp_shapes && m_shape.at_end ()) { - next_shape (); + next_shape (receiver); } } } void -RecursiveShapeIterator::next_shape () const +RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const { while (at_end ()) { @@ -674,10 +674,10 @@ RecursiveShapeIterator::next_shape () const if (is_empty) { ++m_inst; - new_inst (); + new_inst (receiver); } else { - down (); + down (receiver); } } else { @@ -688,12 +688,14 @@ RecursiveShapeIterator::next_shape () const } // no more instances: up and next instance - up (); + up (receiver); ++m_inst_array; + new_inst_member (receiver); + if (m_inst_array.at_end ()) { ++m_inst; - new_inst (); + new_inst (receiver); } } @@ -704,7 +706,7 @@ RecursiveShapeIterator::next_shape () const } void -RecursiveShapeIterator::down () const +RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { m_trans_stack.push_back (m_trans); m_cells.push_back (mp_cell); @@ -761,12 +763,24 @@ RecursiveShapeIterator::down () const } - new_cell (); + if (receiver && ! receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ())) { + if (m_has_layers) { + m_current_layer = m_layers.size (); + } + m_shape = shape_iterator (); + m_inst = inst_iterator (); + } else { + new_cell (receiver); + } } void -RecursiveShapeIterator::up () const +RecursiveShapeIterator::up (RecursiveShapeReceiver *receiver) const { + if (receiver) { + receiver->leave_cell (this, cell ()); + } + m_shape = shape_iterator (); m_shape_quad_id = 0; @@ -823,7 +837,7 @@ RecursiveShapeIterator::new_layer () const } void -RecursiveShapeIterator::new_cell () const +RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const { if (m_has_layers) { m_current_layer = 0; @@ -847,11 +861,11 @@ RecursiveShapeIterator::new_cell () const skip_inst_iter_for_complex_region (); } - new_inst (); + new_inst (receiver); } void -RecursiveShapeIterator::new_inst () const +RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const { // look for the next instance with a non-empty array iterator. The latter can be // empty because we use a per-layer box converter for that case what we don't for the @@ -866,12 +880,21 @@ RecursiveShapeIterator::new_inst () const } } - if (m_local_region_stack.back () != box_type::world ()) { + bool all_of_instance = true; + if (m_local_region_stack.back () != box_type::world () && ! m_inst->cell_inst ().bbox (m_box_convert).inside (m_local_region_stack.back ())) { m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + all_of_instance = false; } else { - m_inst_array = m_inst->cell_inst ().begin (); + m_inst_array = m_inst->cell_inst ().begin (); + // TODO: optimization potential: only report all_of_instance == false, if not entirely within the complex region + all_of_instance = m_local_complex_region_stack.empty (); } + if (receiver) { + receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + } + + new_inst_member (receiver); if (! m_inst_array.at_end ()) { break; } else { @@ -881,6 +904,28 @@ RecursiveShapeIterator::new_inst () const } } +void +RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const +{ + if (! m_local_complex_region_stack.empty ()) { + + // skip instance array members not part of the complex region + while (! m_inst_array.at_end ()) { + db::Box ia_box = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ()); + if (! is_outside_complex_region (ia_box)) { + break; + } else { + ++m_inst_array; + } + } + + } + + if (! m_inst_array.at_end () && receiver) { + receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + } +} + bool RecursiveShapeIterator::is_outside_complex_region (const db::Box &box) const { @@ -891,5 +936,23 @@ RecursiveShapeIterator::is_outside_complex_region (const db::Box &box) const } } +void +RecursiveShapeIterator::push (RecursiveShapeReceiver *receiver) +{ + // force reset so we can validate with a receiver + reset (); + + receiver->begin (this); + + validate (receiver); + + while (! at_end ()) { + receiver->shape (this, *m_shape, m_trans); + next (receiver); + } + + receiver->end (this); +} + } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 970085b63..de4103630 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -38,6 +38,7 @@ namespace db { class Region; +class RecursiveShapeReceiver; /** * @brief An iterator delivering shapes that touch or overlap the given region recursively @@ -479,7 +480,7 @@ public: */ unsigned int layer () const { - validate (); + validate (0); return m_layer; } @@ -514,7 +515,7 @@ public: */ const cplx_trans_type &trans () const { - validate (); + validate (0); return m_trans; } @@ -525,7 +526,7 @@ public: */ unsigned int depth () const { - validate (); + validate (0); return (unsigned int) m_trans_stack.size (); } @@ -537,7 +538,7 @@ public: */ shape_type shape () const { - validate (); + validate (0); return *m_shape; } @@ -548,7 +549,7 @@ public: */ shape_type operator* () const { - validate (); + validate (0); return *m_shape; } @@ -559,7 +560,7 @@ public: */ const shape_type *operator-> () const { - validate (); + validate (0); return m_shape.operator-> (); } @@ -575,7 +576,7 @@ public: */ db::cell_index_type cell_index () const { - validate (); + validate (0); size_t c = reinterpret_cast (mp_cell); return reinterpret_cast (c - (c & size_t (1)))->cell_index (); } @@ -585,7 +586,7 @@ public: */ const cell_type *cell () const { - validate (); + validate (0); size_t c = reinterpret_cast (mp_cell); return reinterpret_cast (c - (c & size_t (1))); } @@ -595,14 +596,17 @@ public: */ RecursiveShapeIterator &operator++() { - next (); + next (0); return *this; } /** - * @brief Increment the iterator + * @brief Increments the iterator */ - void next (); + void next () + { + next (0); + } /** * @brief Comparison of iterators - equality @@ -638,6 +642,20 @@ public: */ std::vector path () const; + /** + * @brief Push-mode delivery + * + * This method will deliver all shapes to the given receiver. + * In contrast to pull mode, this method allows tailoring the + * traversal of the hierarchy tree during iteration. + * For this purpose, the receiver has methods that receive + * events and to some extend may modify the traversal (e.g. + * return value of enter_cell). + * + * See RecursiveShapeReceiver class for more details. + */ + void push (RecursiveShapeReceiver *receiver); + private: std::vector m_layers; bool m_has_layers; @@ -681,14 +699,16 @@ private: void init_region (const box_type ®ion); void skip_shape_iter_for_complex_region () const; void skip_inst_iter_for_complex_region () const; - void validate () const; + void validate (RecursiveShapeReceiver *receiver) const; void start_shapes () const; - void next_shape () const; - void new_inst () const; - void new_cell () const; + void next (RecursiveShapeReceiver *receiver); + void next_shape (RecursiveShapeReceiver *receiver) const; + void new_inst (RecursiveShapeReceiver *receiver) const; + void new_inst_member (RecursiveShapeReceiver *receiver) const; + void new_cell (RecursiveShapeReceiver *receiver) const; void new_layer () const; - void up () const; - void down () const; + void up (RecursiveShapeReceiver *receiver) const; + void down (RecursiveShapeReceiver *receiver) const; bool is_outside_complex_region (const db::Box &box) const; @@ -705,8 +725,99 @@ private: } }; +/** + * @brief A receiver interface for "push" mode + * + * In push mode, the iterator will deliver the shapes and hierarchy transitions + * to this interface. See "RecursiveShapeIterator::push" for details about this + * mode. + * + * The receiver receives events for the start of the delivery, on each cell + * entry and on each instance (followed by a cell entry). It also receives + * the shapes. + */ +class DB_PUBLIC RecursiveShapeReceiver +{ +public: + typedef RecursiveShapeIterator::box_tree_type box_tree_type; + + RecursiveShapeReceiver () { } + virtual ~RecursiveShapeReceiver () { } + + /** + * @brief Called once when the iterator begins pushing + */ + virtual void begin (const RecursiveShapeIterator * /*iter*/) { } + + /** + * @brief Called once after the iterator pushed everything + */ + virtual void end (const RecursiveShapeIterator * /*iter*/) { } + + /** + * @brief Enters a cell + * + * This method is called when the recursive shape iterator + * enters a new cell. This method can return false. In this + * case, the cell is skipped together with it's subcells. + * + * This method is not called for the top cell. When it is called, "iter->trans()" + * will already be updated. + * + * @param iter The iterator + * @param cell The cell which is entered + * @param region The clip box as seen from "cell" or db::Box::world if there is no clip box + * @param complex_region A complex clip region if one is supplied together with "region" + */ + virtual bool enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { return true; } + + /** + * @brief Leaves the current cell + * + * This method is the counterpart for "enter_cell". It is called when traversal of "cell" ended. + */ + virtual void leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/) { } + + /** + * @brief Enters a new instance + * + * This method is called before "enter_cell" and "new_inst_member" is called and will indicate the instance to follow. + * The sequence of events is + * + * new_inst(A) + * new_inst_member(A[0,0]) + * enter_cell(A) + * ... + * leave_cell(A) + * new_inst_member(A[1,0]) + * enter_cell(A) + * ... + * leave_cell(A) + * ... + * new_inst(B) + * ... + * + * The "all" parameter is true, if all instances of the array will be addressed. + */ + virtual void new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { } + + /** + * @brief Enters a new array member of the instance + * + * See "new_inst" for a description. This method adds the "trans" parameter + * which holds the complex transformation for this particular instance of + * the array. + */ + virtual void new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } + + /** + * @brief Delivers a shape + * + * @param trans The transformation which maps the shape to the top cell. + */ + virtual void shape (const RecursiveShapeIterator * /*iter*/, const db::Shape & /*shape*/, const db::ICplxTrans & /*trans*/) { } +}; + } // namespace db #endif - - diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index 27b6964c1..566d02121 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -453,7 +453,7 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS gsi::method ("cell_index", &db::RecursiveShapeIterator::cell_index, "@brief Gets the current cell's index \n" ) + - gsi::method ("next", &db::RecursiveShapeIterator::next, + gsi::method ("next", (void (db::RecursiveShapeIterator::*) ()) &db::RecursiveShapeIterator::next, "@brief Increment the iterator\n" "This moves the iterator to the next shape inside the search scope." ) + diff --git a/src/db/unit_tests/dbRecursiveShapeIterator.cc b/src/db/unit_tests/dbRecursiveShapeIterator.cc index f2503e351..fff996a2c 100644 --- a/src/db/unit_tests/dbRecursiveShapeIterator.cc +++ b/src/db/unit_tests/dbRecursiveShapeIterator.cc @@ -709,6 +709,21 @@ static db::Layout boxes2layout (const std::set &boxes) return l; } +class FlatPusher + : public db::RecursiveShapeReceiver +{ +public: + FlatPusher (std::set *boxes) : mp_boxes (boxes) { } + + void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans) + { + mp_boxes->insert (trans * shape.bbox ()); + } + +private: + std::set *mp_boxes; +}; + TEST(4) { // Big fun @@ -751,6 +766,16 @@ TEST(4) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, search_box, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + db::Box search_box2 (500, 500, 1000, 1000); selected_boxes.clear (); @@ -772,6 +797,16 @@ TEST(4) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, reg, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); } TEST(5) @@ -819,6 +854,16 @@ TEST(5) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, search_box, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + db::Box search_box2 (500, 500, 1000, 1000); selected_boxes.clear (); @@ -840,4 +885,310 @@ TEST(5) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, reg, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); +} + +class LoggingReceiver + : public db::RecursiveShapeReceiver +{ +public: + LoggingReceiver () { } + + const std::string &text () const { return m_text; } + + virtual void begin (const db::RecursiveShapeIterator * /*iter*/) { m_text += "begin\n"; } + virtual void end (const db::RecursiveShapeIterator * /*iter*/) { m_text += "end\n"; } + + virtual bool enter_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + m_text += std::string ("enter_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; + return true; + } + + virtual void leave_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell) + { + m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; + } + + virtual void new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + { + m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); + if (all) { + m_text += ",all"; + } + m_text += ")\n"; + } + + virtual void new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (trans) + ")\n"; + } + + virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans) + { + m_text += "shape(" + shape.to_string () + "," + tl::to_string (trans) + ")\n"; + } + +private: + std::string m_text; +}; + +class ReceiverRejectingACell + : public LoggingReceiver +{ +public: + ReceiverRejectingACell (db::cell_index_type rejected) : m_rejected (rejected) { } + + virtual bool enter_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region) + { + LoggingReceiver::enter_cell (iter, cell, region, complex_region); + return cell->cell_index () != m_rejected; + } + +private: + db::cell_index_type m_rejected; +}; + +// Push mode with cells +TEST(10) +{ + db::Manager m; + db::Layout g (&m); + g.insert_layer(0); + + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + db::Cell &c2 (g.cell (g.add_cell ())); + + db::Box b (1000, -500, 2000, 500); + c2.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt, db::Vector (0, 6000), db::Vector (6000, 0), 2, 2)); + c1.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), tt, db::Vector (0, 2000), db::Vector (3000, 1000), 2, 2)); + + LoggingReceiver lr1; + db::RecursiveShapeIterator i1 (g, c0, 0); + i1.push (&lr1); + + EXPECT_EQ (lr1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,6000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,6000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACell rr1 (c2.cell_index ()); + db::RecursiveShapeIterator ir1 (g, c0, 0); + ir1.push (&rr1); + + EXPECT_EQ (rr1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACell rr2 (c1.cell_index ()); + db::RecursiveShapeIterator ir2 (g, c0, 0); + ir2.push (&rr2); + + EXPECT_EQ (rr2.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "enter_cell($2)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000)\n" + "enter_cell($2)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0)\n" + "enter_cell($2)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000)\n" + "enter_cell($2)\n" + "leave_cell($2)\n" + "end\n" + ); + + LoggingReceiver lr2; + db::RecursiveShapeIterator i2 (g, c0, 0, db::Box (0, 0, 5000, 5000)); + i2.push (&lr2); + + EXPECT_EQ (lr2.text (), + "begin\n" + "new_inst($2)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); } From 2bfccca4628207c1238fb7207ee099d708b9ce52 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 13 Nov 2018 23:40:08 +0100 Subject: [PATCH 051/335] WIP: enhanced recursive shape iterator's push mode. --- src/db/db/dbRecursiveShapeIterator.cc | 41 +++---- src/db/db/dbRecursiveShapeIterator.h | 15 +-- src/db/unit_tests/dbRecursiveShapeIterator.cc | 100 ++++++++++++------ 3 files changed, 98 insertions(+), 58 deletions(-) diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 9942a0c2c..2c9216776 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -763,15 +763,11 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const } - if (receiver && ! receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ())) { - if (m_has_layers) { - m_current_layer = m_layers.size (); - } - m_shape = shape_iterator (); - m_inst = inst_iterator (); - } else { - new_cell (receiver); + if (receiver) { + receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); } + + new_cell (receiver); } void @@ -880,21 +876,24 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const } } - bool all_of_instance = true; if (m_local_region_stack.back () != box_type::world () && ! m_inst->cell_inst ().bbox (m_box_convert).inside (m_local_region_stack.back ())) { - m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); - all_of_instance = false; + if (receiver && ! receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), false)) { + m_inst_array = inst_array_iterator (); + } else { + m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + } } else { - m_inst_array = m_inst->cell_inst ().begin (); // TODO: optimization potential: only report all_of_instance == false, if not entirely within the complex region - all_of_instance = m_local_complex_region_stack.empty (); - } - - if (receiver) { - receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + bool all_of_instance = m_local_complex_region_stack.empty (); + if (receiver && ! receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance)) { + m_inst_array = inst_array_iterator (); + } else { + m_inst_array = m_inst->cell_inst ().begin (); + } } new_inst_member (receiver); + if (! m_inst_array.at_end ()) { break; } else { @@ -921,8 +920,12 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const } - if (! m_inst_array.at_end () && receiver) { - receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + while (! m_inst_array.at_end () && receiver) { + if (receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ())) { + break; + } else { + ++m_inst_array; + } } } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index de4103630..c992f085c 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -758,10 +758,7 @@ public: * @brief Enters a cell * * This method is called when the recursive shape iterator - * enters a new cell. This method can return false. In this - * case, the cell is skipped together with it's subcells. - * - * This method is not called for the top cell. When it is called, "iter->trans()" + * enters a new cell. It is not called for the top cell. When it is called, "iter->trans()" * will already be updated. * * @param iter The iterator @@ -769,7 +766,7 @@ public: * @param region The clip box as seen from "cell" or db::Box::world if there is no clip box * @param complex_region A complex clip region if one is supplied together with "region" */ - virtual bool enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { return true; } + virtual void enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } /** * @brief Leaves the current cell @@ -798,8 +795,10 @@ public: * ... * * The "all" parameter is true, if all instances of the array will be addressed. + * + * If this method returns false, the instance (the whole array) is skipped and the cell is not entered. */ - virtual void new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { } + virtual bool new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } /** * @brief Enters a new array member of the instance @@ -807,8 +806,10 @@ public: * See "new_inst" for a description. This method adds the "trans" parameter * which holds the complex transformation for this particular instance of * the array. + * + * If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered. */ - virtual void new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } + virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { return true; } /** * @brief Delivers a shape diff --git a/src/db/unit_tests/dbRecursiveShapeIterator.cc b/src/db/unit_tests/dbRecursiveShapeIterator.cc index fff996a2c..03dc7f918 100644 --- a/src/db/unit_tests/dbRecursiveShapeIterator.cc +++ b/src/db/unit_tests/dbRecursiveShapeIterator.cc @@ -908,10 +908,9 @@ public: virtual void begin (const db::RecursiveShapeIterator * /*iter*/) { m_text += "begin\n"; } virtual void end (const db::RecursiveShapeIterator * /*iter*/) { m_text += "end\n"; } - virtual bool enter_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + virtual void enter_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { m_text += std::string ("enter_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; - return true; } virtual void leave_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell) @@ -919,18 +918,20 @@ public: m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; } - virtual void new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual bool new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) { m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); if (all) { m_text += ",all"; } m_text += ")\n"; + return true; } - virtual void new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (trans) + ")\n"; + return true; } virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans) @@ -942,22 +943,39 @@ private: std::string m_text; }; -class ReceiverRejectingACell +class ReceiverRejectingACellInstanceArray : public LoggingReceiver { public: - ReceiverRejectingACell (db::cell_index_type rejected) : m_rejected (rejected) { } + ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { } - virtual bool enter_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region) + virtual bool new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) { - LoggingReceiver::enter_cell (iter, cell, region, complex_region); - return cell->cell_index () != m_rejected; + LoggingReceiver::new_inst (iter, inst, region, complex_region, all); + return inst.object ().cell_index () != m_rejected; } private: db::cell_index_type m_rejected; }; +class ReceiverRejectingACellInstance + : public LoggingReceiver +{ +public: + ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { } + + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region) + { + LoggingReceiver::new_inst_member (iter, inst, trans, region, complex_region); + return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected; + } + +private: + db::cell_index_type m_rejected; + db::ICplxTrans m_trans_rejected; +}; + // Push mode with cells TEST(10) { @@ -1066,7 +1084,7 @@ TEST(10) "end\n" ); - ReceiverRejectingACell rr1 (c2.cell_index ()); + ReceiverRejectingACellInstanceArray rr1 (c2.cell_index ()); db::RecursiveShapeIterator ir1 (g, c0, 0); ir1.push (&rr1); @@ -1076,89 +1094,107 @@ TEST(10) "new_inst_member($2,r0 *1 0,0)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" - "enter_cell($3)\n" - "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACellInstance rri1 (c2.cell_index (), db::ICplxTrans ()); + db::RecursiveShapeIterator iri1 (g, c0, 0); + iri1.push (&rri1); + + EXPECT_EQ (rri1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" // -> skipped "new_inst_member($3,r0 *1 0,2000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" "leave_cell($3)\n" "new_inst_member($3,r0 *1 3000,1000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" "leave_cell($3)\n" "new_inst_member($3,r0 *1 3000,3000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" "leave_cell($3)\n" "leave_cell($2)\n" "new_inst_member($2,r0 *1 0,6000)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "new_inst_member($3,r0 *1 0,0)\n" - "enter_cell($3)\n" - "leave_cell($3)\n" "new_inst_member($3,r0 *1 0,2000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" "leave_cell($3)\n" "new_inst_member($3,r0 *1 3000,1000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" "leave_cell($3)\n" "new_inst_member($3,r0 *1 3000,3000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" "leave_cell($3)\n" "leave_cell($2)\n" "new_inst_member($2,r0 *1 6000,0)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "new_inst_member($3,r0 *1 0,0)\n" - "enter_cell($3)\n" - "leave_cell($3)\n" "new_inst_member($3,r0 *1 0,2000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" "leave_cell($3)\n" "new_inst_member($3,r0 *1 3000,1000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" "leave_cell($3)\n" "new_inst_member($3,r0 *1 3000,3000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" "leave_cell($3)\n" "leave_cell($2)\n" "new_inst_member($2,r0 *1 6000,6000)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "new_inst_member($3,r0 *1 0,0)\n" - "enter_cell($3)\n" - "leave_cell($3)\n" "new_inst_member($3,r0 *1 0,2000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" "leave_cell($3)\n" "new_inst_member($3,r0 *1 3000,1000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" "leave_cell($3)\n" "new_inst_member($3,r0 *1 3000,3000)\n" "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" "leave_cell($3)\n" "leave_cell($2)\n" "end\n" ); - ReceiverRejectingACell rr2 (c1.cell_index ()); + ReceiverRejectingACellInstanceArray rr2 (c1.cell_index ()); db::RecursiveShapeIterator ir2 (g, c0, 0); ir2.push (&rr2); EXPECT_EQ (rr2.text (), "begin\n" "new_inst($2,all)\n" - "new_inst_member($2,r0 *1 0,0)\n" - "enter_cell($2)\n" - "leave_cell($2)\n" - "new_inst_member($2,r0 *1 0,6000)\n" - "enter_cell($2)\n" - "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,0)\n" - "enter_cell($2)\n" - "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,6000)\n" - "enter_cell($2)\n" - "leave_cell($2)\n" "end\n" ); From 6f4988a76480b55fbfe8d06853c940e9b6209afb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 14 Nov 2018 02:22:53 +0100 Subject: [PATCH 052/335] WIP: first version hierarchy builder --- src/db/db/dbHierarchyBuilder.cc | 304 +++++++++++++++++++++++++++ src/db/db/dbHierarchyBuilder.h | 92 ++++++++ src/db/db/dbLayout.h | 4 +- src/db/db/dbRecursiveShapeIterator.h | 48 ++++- 4 files changed, 437 insertions(+), 11 deletions(-) create mode 100644 src/db/db/dbHierarchyBuilder.cc create mode 100644 src/db/db/dbHierarchyBuilder.h diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc new file mode 100644 index 000000000..388da34e3 --- /dev/null +++ b/src/db/db/dbHierarchyBuilder.cc @@ -0,0 +1,304 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbRecursiveShapeIterator.h" +#include "dbHierarchyBuilder.h" +#include "dbClip.h" +#include "dbRegion.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------- + +int +compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2) +{ + // basic source (layout, top_cell) needs to be the same of course + if (iter1.layout () != iter2.layout ()) { + // NOTE: pointer compare :-( + return iter1.layout () < iter2.layout () ? -1 : 1; + } + if (iter1.top_cell ()->cell_index () != iter2.top_cell ()->cell_index ()) { + return iter1.top_cell ()->cell_index () < iter2.top_cell ()->cell_index () ? -1 : 1; + } + + // max depth controls the main hierarchical appearance + if (iter1.max_depth () != iter2.max_depth ()) { + return iter1.max_depth () < iter2.max_depth () ? -1 : 1; + } + + // if a region is set, the hierarchical appearance is the same only if the layers and + // complex region are indentical + if ((iter1.region () == db::Box::world ()) != (iter2.region () == db::Box::world ())) { + return (iter1.region () == db::Box::world ()) < (iter2.region () == db::Box::world ()) ? -1 : 1; + } + if (iter1.region () != db::Box::world ()) { + if (iter1.has_complex_region () != iter2.has_complex_region ()) { + return iter1.has_complex_region () < iter2.has_complex_region () ? -1 : 1; + } + if (iter1.has_complex_region () && iter1.complex_region () != iter2.complex_region ()) { + return iter1.complex_region () < iter2.complex_region () ? -1 : 1; + } + if (iter1.multiple_layers () != iter2.multiple_layers ()) { + return iter1.multiple_layers () < iter2.multiple_layers () ? -1 : 1; + } + if (iter1.multiple_layers ()) { + if (iter1.layers () != iter2.layers ()) { + return iter1.layers () < iter2.layers (); + } + } else { + if (iter1.layer () != iter2.layer ()) { + return iter1.layer () < iter2.layer (); + } + } + } + + return 0; +} + +// ------------------------------------------------------------------------------------------- + +static bool is_outside (const db::Box &obj, const std::set ®ion) +{ + if (region.empty ()) { + return false; + } + + for (std::set::const_iterator b = region.begin (); b != region.end (); ++b) { + if (obj.touches (*b)) { + return false; + } + } + + return true; +} + +static bool is_inside (const db::Box &obj, const std::set ®ion) +{ + if (region.empty ()) { + return true; + } + + for (std::set::const_iterator b = region.begin (); b != region.end (); ++b) { + if (obj.inside (*b)) { + return true; + } + } + + if (is_outside (obj, region)) { + return false; + } else { + + // TODO: basically a detailed analysis is required here + return false; + + } +} + +/** + * @brief Computes the clip variant (a box set) from a cell bbox, a region and a complex region (optional) + */ +static std::set compute_clip_variant (const db::Box &cell_bbox, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) +{ + std::set clip_variant; + if (region != db::Box::world () && ! cell_bbox.inside (region)) { + + db::Box rect_box = region & cell_bbox; + + if (complex_region) { + + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert ()); ! cr.at_end (); ++cr) { + if (rect_box.overlaps (*cr)) { + clip_variant.insert (rect_box * *cr); + } + } + + if (clip_variant.empty ()) { + // an empty clip variant should not happen, but who knows + clip_variant.insert (db::Box ()); + } + + } else { + clip_variant.insert (rect_box); + } + + } + + return clip_variant; +} + +static void insert_clipped (const db::Box &box, const std::set &clip, db::Shapes &shapes) +{ + for (std::set::const_iterator b = clip.begin (); b != clip.end (); ++b) { + db::Box bb = *b & box; + if (! bb.empty ()) { + shapes.insert (bb); + } + } +} + +static void insert_clipped (const db::Polygon &poly, const std::set &clip, db::Shapes &shapes) +{ + // TODO: is this good way to clip a polygon at a complex boundary? + for (std::set::const_iterator b = clip.begin (); b != clip.end (); ++b) { + std::vector clipped_poly; + db::clip_poly (poly, *b, clipped_poly); + for (std::vector::const_iterator p = clipped_poly.begin (); p != clipped_poly.end (); ++p) { + shapes.insert (*p); + } + } +} + + +HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, bool clip_shapes) + : mp_target (target), m_clip_shapes (clip_shapes), m_initial_pass (true), m_target_layer (target_layer) +{ + // .. nothing yet .. +} + +void +HierarchyBuilder::begin (const RecursiveShapeIterator *iter) +{ + if (m_initial_pass) { + m_ref_iter = *iter; + } else { + tl_assert (compare_iterators_with_respect_to_target_hierarchy (m_ref_iter, *iter) == 0); + } + + m_cells_seen.clear (); + m_cm_entry = cell_map_type::const_iterator (); + + m_cell_stack.clear (); + + db::Cell &new_top = mp_target->cell (mp_target->add_cell (iter->layout ()->cell_name (iter->top_cell ()->cell_index ()))); + m_cell_stack.push_back (&new_top); +} + +void +HierarchyBuilder::end (const RecursiveShapeIterator * /*iter*/) +{ + m_initial_pass = false; + m_cells_seen.clear (); + m_cell_stack.pop_back (); +} + +void +HierarchyBuilder::enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) +{ + tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ()); + m_cells_seen.insert (m_cm_entry->first); + + m_cell_stack.push_back (&mp_target->cell (m_cm_entry->second)); +} + +void +HierarchyBuilder::leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/) +{ + m_cell_stack.pop_back (); +} + +bool +HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) +{ + if (all) { + + std::pair > key (inst.object ().cell_index (), std::set ()); + m_cm_entry = m_cell_map.find (key); + + if (m_initial_pass) { + + if (m_cm_entry == m_cell_map.end ()) { + db::cell_index_type new_cell = mp_target->add_cell (iter->layout ()->cell_name (inst.object ().cell_index ())); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + } + + db::CellInstArray new_inst = inst; + new_inst.object () = db::CellInst (m_cm_entry->second); + m_cell_stack.back ()->insert (new_inst); + + } + + return (m_cells_seen.find (key) == m_cells_seen.end ()); + + } else { + // iterate by instance array members + return true; + } +} + +bool +HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region) +{ + db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox (); + std::set clip_variant = compute_clip_variant (cell_bbox, region, complex_region); + + std::pair > key (inst.object ().cell_index (), clip_variant); + m_cm_entry = m_cell_map.find (key); + + if (m_initial_pass) { + + if (m_cm_entry == m_cell_map.end ()) { + std::string suffix; + if (! key.second.empty ()) { + suffix = "$CLIP_VAR"; + } + db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ()); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + } + + db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans); + m_cell_stack.back ()->insert (new_inst); + + } + + return (m_cells_seen.find (key) == m_cells_seen.end ()); +} + +void +HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/) +{ + tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ()); + + // TODO: property mapping? + + db::Shapes &shapes = m_cell_stack.back ()->shapes (m_target_layer); + + if (m_cm_entry->first.second.empty () || is_inside (shape.bbox (), m_cm_entry->first.second)) { + + shapes.insert (shape); + + } else if (! is_outside (shape.bbox (), m_cm_entry->first.second)) { + + // clip the shape if required + if (! m_clip_shapes || shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { + shapes.insert (shape); + } else if (shape.is_box ()) { + insert_clipped (shape.box (), m_cm_entry->first.second, shapes); + } else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { + insert_clipped (shape.polygon (), m_cm_entry->first.second, shapes); + } + + } +} + +} diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h new file mode 100644 index 000000000..c452beb25 --- /dev/null +++ b/src/db/db/dbHierarchyBuilder.h @@ -0,0 +1,92 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbHierarchyBuilder +#define HDR_dbHierarchyBuilder + +#include "dbCommon.h" + +#include "dbRecursiveShapeIterator.h" +#include "dbLayout.h" + +#include +#include +#include + +namespace db +{ + +/** + * @brief A helper function comparing two recursive shape iterators for compatibility with respect to hierarchy building + * + * This function will return -1, 0 or 1 depending on whether the two iterators + * can be used with the same builder (0) or whether they are less (-1) or greater (1). + */ +int compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2); + +/** + * @brief A class building a hierarchy from a recursive shape iterator in push mode + * + * This class is a RecursiveShapeReceiver which acts on the hierarchy events and + * uses them to rebuild a hierarchy in the target layout. In can be used multiple + * times and will reuse the hierarchy as far as possible. + * + * The hierarchy builder can form clip variants for cells and clip the shapes + * according to the selected region. + * + * NOTE: the hierarchy build should not be used in multiple passes with regions + * as the hierarchy is sampled in the first pass and the hierarchy builder will + * rely on precisely the same hierarchy arrangement. This is not given with + * region selections. + */ +class HierarchyBuilder + : public db::RecursiveShapeReceiver +{ +public: + + typedef std::map >, db::cell_index_type> cell_map_type; + + HierarchyBuilder (db::Layout *target, unsigned int target_layer, bool clip_shapes); + + virtual void begin (const RecursiveShapeIterator *iter); + virtual void end (const RecursiveShapeIterator * /*iter*/); + virtual void enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/); + virtual void leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/); + virtual bool new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); + virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &trans); + +private: + tl::weak_ptr mp_target; + bool m_clip_shapes; + bool m_initial_pass; + db::RecursiveShapeIterator m_ref_iter; + cell_map_type m_cell_map; + std::set m_cells_seen; + cell_map_type::const_iterator m_cm_entry; + unsigned int m_target_layer; + std::vector m_cell_stack; +}; + +} + +#endif diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 5dd824c64..e1724657d 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -39,6 +39,7 @@ #include "tlVector.h" #include "tlString.h" #include "tlThreads.h" +#include "tlObject.h" #include "gsi.h" #include @@ -457,7 +458,8 @@ public: class DB_PUBLIC Layout : public db::Object, public db::LayoutStateModel, - public gsi::ObjectBase + public gsi::ObjectBase, + public tl::Object { public: typedef db::Box box_type; diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index c992f085c..ce54b7c2f 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -266,6 +266,29 @@ public: return m_max_depth; } + /** + * @brief Specify the minimum hierarchy depth to look into + * + * A depth of 0 instructs the iterator to deliver shapes from the top level. + * 1 instructs to deliver shapes from the first child level. + * The minimum depth must be specified before the shapes are being retrieved. + */ + void min_depth (int depth) + { + if (m_min_depth != depth) { + m_min_depth = depth; + m_needs_reinit = true; + } + } + + /** + * @brief Gets the minimum hierarchy depth to search for + */ + int min_depth () const + { + return m_min_depth; + } + /** * @brief Gets the iterated shapes * @@ -418,18 +441,23 @@ public: void reset_selection (); /** - * @brief Specify the minimum hierarchy depth to look into + * @brief Returns the cells in the "enable" selection * - * A depth of 0 instructs the iterator to deliver shapes from the top level. - * 1 instructs to deliver shapes from the first child level. - * The minimum depth must be specified before the shapes are being retrieved. + * Cells in this set make the iterator become active, while cells in the + * disable selection make the iterator inactive. Only when active, the + * iterator will deliver shapes. */ - void min_depth (int depth) - { - if (m_min_depth != depth) { - m_min_depth = depth; - m_needs_reinit = true; - } + const std::set &enables () const + { + return m_start; + } + + /** + * @brief Returns the cells in the "disable" selection + */ + const std::set &disables () const + { + return m_stop; } /** From 3f8825cfd1faef21c9588551e281d1b73f906fcd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 15 Nov 2018 22:50:02 +0100 Subject: [PATCH 053/335] WIP: Improved design of HierarchyBuilder, added tests. --- src/db/db/db.pro | 6 +- src/db/db/dbDeepShapeStore.cc | 37 +- src/db/db/dbDeepShapeStore.h | 26 +- src/db/db/dbHierarchyBuilder.cc | 442 +++++++++++++----- src/db/db/dbHierarchyBuilder.h | 150 +++++- src/db/db/dbLayout.cc | 18 + src/db/db/dbLayout.h | 8 + src/db/db/dbRecursiveShapeIterator.cc | 40 +- src/db/db/dbRecursiveShapeIterator.h | 55 ++- src/db/db/gsiDeclDbLayout.cc | 27 +- src/db/unit_tests/dbHierarchyBuilderTests.cc | 337 +++++++++++++ src/db/unit_tests/dbRecursiveShapeIterator.cc | 166 ++++--- src/db/unit_tests/unit_tests.pro | 3 +- src/tl/tl/tlObject.cc | 4 +- src/tl/tl/tlObject.h | 4 +- testdata/algo/hierarchy_builder_au1.gds | Bin 0 -> 2762 bytes testdata/algo/hierarchy_builder_au2a.gds | Bin 0 -> 11526 bytes testdata/algo/hierarchy_builder_au2b.gds | Bin 0 -> 11614 bytes testdata/algo/hierarchy_builder_au2c.gds | Bin 0 -> 11902 bytes testdata/algo/hierarchy_builder_au2d.gds | Bin 0 -> 11536 bytes testdata/algo/hierarchy_builder_au2e.gds | Bin 0 -> 942 bytes testdata/algo/hierarchy_builder_au3a.gds | Bin 0 -> 1172 bytes testdata/algo/hierarchy_builder_au4a.gds | Bin 0 -> 82618 bytes testdata/algo/hierarchy_builder_l1.gds | Bin 0 -> 2762 bytes testdata/algo/hierarchy_builder_l2.gds | Bin 0 -> 268 bytes testdata/algo/hierarchy_builder_l3.gds | Bin 0 -> 2858 bytes 26 files changed, 1091 insertions(+), 232 deletions(-) create mode 100644 src/db/unit_tests/dbHierarchyBuilderTests.cc create mode 100644 testdata/algo/hierarchy_builder_au1.gds create mode 100644 testdata/algo/hierarchy_builder_au2a.gds create mode 100644 testdata/algo/hierarchy_builder_au2b.gds create mode 100644 testdata/algo/hierarchy_builder_au2c.gds create mode 100644 testdata/algo/hierarchy_builder_au2d.gds create mode 100644 testdata/algo/hierarchy_builder_au2e.gds create mode 100644 testdata/algo/hierarchy_builder_au3a.gds create mode 100644 testdata/algo/hierarchy_builder_au4a.gds create mode 100644 testdata/algo/hierarchy_builder_l1.gds create mode 100644 testdata/algo/hierarchy_builder_l2.gds create mode 100644 testdata/algo/hierarchy_builder_l3.gds diff --git a/src/db/db/db.pro b/src/db/db/db.pro index ff156a235..f32306b99 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -136,7 +136,8 @@ SOURCES = \ dbFlatEdgePairs.cc \ dbOriginalLayerEdgePairs.cc \ dbEdgePairsDelegate.cc \ - dbDeepShapeStore.cc + dbDeepShapeStore.cc \ + dbHierarchyBuilder.cc HEADERS = \ dbArray.h \ @@ -242,7 +243,8 @@ HEADERS = \ dbFlatEdgePairs.h \ dbOriginalLayerEdgePairs.h \ dbEdgePairsDelegate.h \ - dbDeepShapeStore.h + dbDeepShapeStore.h \ + dbHierarchyBuilder.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index e51bd5a3a..abb1469e8 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -21,4 +21,39 @@ */ -// ... +#include "dbDeepShapeStore.h" + +namespace db +{ + +DeepLayer::DeepLayer (const DeepLayer &x) + : mp_store (x.mp_store), m_layout (x.m_layout), m_layer (x.m_layer) +{ + // .. nothing yet .. +} + +DeepLayer::DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer) + : mp_store (store), m_layout (layout), m_layer (layer) +{ + // .. nothing yet .. +} + + + +DeepShapeStore::DeepShapeStore () +{ + // @@@ +} + +DeepShapeStore::~DeepShapeStore () +{ + // @@@ +} + +DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count) +{ + return DeepLayer (0, 0, 0); // @@@ +} + +} + diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 2a32c2439..f76805fd1 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -30,6 +30,9 @@ #include "dbLayout.h" #include "dbRecursiveShapeIterator.h" +#include +#include + namespace db { class DeepShapeStore; @@ -81,9 +84,6 @@ private: */ DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer); - unsigned int layout () const { return m_layout; } - unsigned int layer () const { return m_layer; } - tl::weak_ptr mp_store; unsigned int m_layout; unsigned int m_layer; @@ -92,7 +92,7 @@ private: /** * @brief The "deep shape store" is a working model for the hierarchical ("deep") processor * - * The deep shape store keep temporary data for the deep shape processor. + * The deep shape store keeps temporary data for the deep shape processor. * It mainly consists of layout objects holding the hierarchy trees and layers * for the actual shapes. * @@ -110,10 +110,28 @@ public: */ DeepShapeStore (); + /** + * @brief The destructor + */ + ~DeepShapeStore (); + + /** + * @brief Inserts a polygon layer into the deep shape store + * + * This method will create a new layer inside the deep shape store as a + * working copy of the original layer. Preparation involves re-shaping + * the polygons so their bounding box is a better approximation and the + * polygon complexity is reduced. For this, the polygons are split + * into parts satisfying the area ratio (bounding box vs. polygon area) + * and maximum vertex count constraints. + */ + DeepLayer create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio = 3.0, size_t max_vertex_count = 16); + private: // no copying DeepShapeStore (const DeepShapeStore &); DeepShapeStore &operator= (const DeepShapeStore &); + }; } diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 388da34e3..be5945412 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -24,10 +24,13 @@ #include "dbHierarchyBuilder.h" #include "dbClip.h" #include "dbRegion.h" +#include "dbPolygonTools.h" namespace db { +static HierarchyBuilderShapeInserter def_inserter; + // ------------------------------------------------------------------------------------------- int @@ -78,104 +81,82 @@ compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIter // ------------------------------------------------------------------------------------------- -static bool is_outside (const db::Box &obj, const std::set ®ion) -{ - if (region.empty ()) { - return false; - } - - for (std::set::const_iterator b = region.begin (); b != region.end (); ++b) { - if (obj.touches (*b)) { - return false; - } - } - - return true; -} - -static bool is_inside (const db::Box &obj, const std::set ®ion) -{ - if (region.empty ()) { - return true; - } - - for (std::set::const_iterator b = region.begin (); b != region.end (); ++b) { - if (obj.inside (*b)) { - return true; - } - } - - if (is_outside (obj, region)) { - return false; - } else { - - // TODO: basically a detailed analysis is required here - return false; - - } -} - /** * @brief Computes the clip variant (a box set) from a cell bbox, a region and a complex region (optional) */ -static std::set compute_clip_variant (const db::Box &cell_bbox, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) +static std::pair > compute_clip_variant (const db::Box &cell_bbox, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) { + if (region == db::Box::world ()) { + return std::make_pair (true, std::set ()); + } + + db::ICplxTrans trans_inv (trans.inverted ()); + db::Box region_in_cell = region.transformed (trans_inv); + std::set clip_variant; - if (region != db::Box::world () && ! cell_bbox.inside (region)) { + if (! cell_bbox.overlaps (region_in_cell)) { + // an empty clip variant should not happen, but who knows + return std::make_pair (false, std::set ()); + } - db::Box rect_box = region & cell_bbox; + db::Box rect_box = region_in_cell & cell_bbox; - if (complex_region) { + if (complex_region) { - for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert ()); ! cr.at_end (); ++cr) { - if (rect_box.overlaps (*cr)) { - clip_variant.insert (rect_box * *cr); - } + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (region, db::box_convert ()); ! cr.at_end (); ++cr) { + db::Box cr_in_cell = (*cr).transformed (trans_inv); + if (rect_box.overlaps (cr_in_cell)) { + clip_variant.insert (rect_box * cr_in_cell); } - - if (clip_variant.empty ()) { - // an empty clip variant should not happen, but who knows - clip_variant.insert (db::Box ()); - } - - } else { - clip_variant.insert (rect_box); } + if (clip_variant.empty ()) { + // an empty clip variant should not happen, but who knows + return std::make_pair (false, std::set ()); + } + + } else { + clip_variant.insert (rect_box); } - return clip_variant; + return std::make_pair (true, clip_variant); } -static void insert_clipped (const db::Box &box, const std::set &clip, db::Shapes &shapes) +HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, HierarchyBuilderShapeReceiver *pipe) + : mp_target (target), m_initial_pass (true), m_target_layer (target_layer) { - for (std::set::const_iterator b = clip.begin (); b != clip.end (); ++b) { - db::Box bb = *b & box; - if (! bb.empty ()) { - shapes.insert (bb); - } - } + mp_pipe = pipe ? pipe : &def_inserter; } -static void insert_clipped (const db::Polygon &poly, const std::set &clip, db::Shapes &shapes) +HierarchyBuilder::HierarchyBuilder (db::Layout *target, HierarchyBuilderShapeReceiver *pipe) + : mp_target (target), m_initial_pass (true), m_target_layer (0) { - // TODO: is this good way to clip a polygon at a complex boundary? - for (std::set::const_iterator b = clip.begin (); b != clip.end (); ++b) { - std::vector clipped_poly; - db::clip_poly (poly, *b, clipped_poly); - for (std::vector::const_iterator p = clipped_poly.begin (); p != clipped_poly.end (); ++p) { - shapes.insert (*p); - } - } + mp_pipe = pipe ? pipe : &def_inserter; } - -HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, bool clip_shapes) - : mp_target (target), m_clip_shapes (clip_shapes), m_initial_pass (true), m_target_layer (target_layer) +HierarchyBuilder::~HierarchyBuilder () { // .. nothing yet .. } +void +HierarchyBuilder::set_shape_receiver (HierarchyBuilderShapeReceiver *pipe) +{ + mp_pipe = pipe; +} + +void +HierarchyBuilder::reset () +{ + m_initial_pass = true; + mp_initial_cell = 0; + + m_cell_map.clear (); + m_cells_seen.clear (); + m_cell_stack.clear (); + m_cm_entry = cell_map_type::const_iterator (); +} + void HierarchyBuilder::begin (const RecursiveShapeIterator *iter) { @@ -185,20 +166,32 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter) tl_assert (compare_iterators_with_respect_to_target_hierarchy (m_ref_iter, *iter) == 0); } - m_cells_seen.clear (); - m_cm_entry = cell_map_type::const_iterator (); - m_cell_stack.clear (); + m_cells_seen.clear (); + + std::pair > key (iter->top_cell ()->cell_index (), std::set ()); + m_cm_entry = m_cell_map.find (key); + if (m_cm_entry == m_cell_map.end ()) { + + db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.first)); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_top_index)).first; + + } + + db::Cell &new_top = mp_target->cell (m_cm_entry->second); + m_cells_seen.insert (key); - db::Cell &new_top = mp_target->cell (mp_target->add_cell (iter->layout ()->cell_name (iter->top_cell ()->cell_index ()))); m_cell_stack.push_back (&new_top); } void HierarchyBuilder::end (const RecursiveShapeIterator * /*iter*/) { + tl_assert (m_cell_stack.size () == 1); + m_initial_pass = false; m_cells_seen.clear (); + mp_initial_cell = m_cell_stack.back (); m_cell_stack.pop_back (); } @@ -217,7 +210,7 @@ HierarchyBuilder::leave_cell (const RecursiveShapeIterator * /*iter*/, const db: m_cell_stack.pop_back (); } -bool +HierarchyBuilder::new_inst_mode HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) { if (all) { @@ -238,67 +231,296 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } - return (m_cells_seen.find (key) == m_cells_seen.end ()); + // To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array. + return (m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; } else { - // iterate by instance array members - return true; + + // Iterate by instance array members + return NI_all; + } } bool -HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region) +HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) { - db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox (); - std::set clip_variant = compute_clip_variant (cell_bbox, region, complex_region); + if (all) { - std::pair > key (inst.object ().cell_index (), clip_variant); - m_cm_entry = m_cell_map.find (key); + return true; - if (m_initial_pass) { + } else { - if (m_cm_entry == m_cell_map.end ()) { - std::string suffix; - if (! key.second.empty ()) { - suffix = "$CLIP_VAR"; - } - db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ()); - m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox (); + std::pair > clip_variant = compute_clip_variant (cell_bbox, trans, region, complex_region); + if (! clip_variant.first) { + return false; } - db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans); - m_cell_stack.back ()->insert (new_inst); + std::pair > key (inst.object ().cell_index (), clip_variant.second); + m_cm_entry = m_cell_map.find (key); + + if (m_initial_pass) { + + if (m_cm_entry == m_cell_map.end ()) { + std::string suffix; + if (! key.second.empty ()) { + suffix = "$CLIP_VAR"; + } + db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ()); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + } + + db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans); + m_cell_stack.back ()->insert (new_inst); + + } + + return (m_cells_seen.find (key) == m_cells_seen.end ()); } - - return (m_cells_seen.find (key) == m_cells_seen.end ()); } void -HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/) +HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/, const db::Box ®ion, const box_tree_type *complex_region) { - tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ()); - - // TODO: property mapping? - db::Shapes &shapes = m_cell_stack.back ()->shapes (m_target_layer); + mp_pipe->push (shape, region, complex_region, &shapes); +} - if (m_cm_entry->first.second.empty () || is_inside (shape.bbox (), m_cm_entry->first.second)) { +// --------------------------------------------------------------------------------------------- - shapes.insert (shape); +ClippingHierarchyBuilderShapeReceiver::ClippingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe) + : mp_pipe (pipe ? pipe : &def_inserter) +{ + // .. nothing yet .. +} - } else if (! is_outside (shape.bbox (), m_cm_entry->first.second)) { +void +ClippingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + static db::Box world = db::Box::world (); + + if (region == world || is_inside (shape.bbox (), region, complex_region)) { + + mp_pipe->push (shape, world, 0, target); + + } else if (! is_outside (shape.bbox (), region, complex_region)) { // clip the shape if required - if (! m_clip_shapes || shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { - shapes.insert (shape); + if (shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { + mp_pipe->push (shape, world, 0, target); } else if (shape.is_box ()) { - insert_clipped (shape.box (), m_cm_entry->first.second, shapes); + insert_clipped (shape.box (), region, complex_region, target); } else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { - insert_clipped (shape.polygon (), m_cm_entry->first.second, shapes); + db::Polygon poly; + shape.polygon (poly); + insert_clipped (poly, region, complex_region, target); } } } +void +ClippingHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + static db::Box world = db::Box::world (); + + if (! complex_region) { + db::Box r = shape & region; + if (! r.empty()) { + mp_pipe->push (r, world, 0, target); + } + } else { + insert_clipped (shape, region, complex_region, target); + } +} + +void +ClippingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + static db::Box world = db::Box::world (); + + if (region == world || (shape.box ().inside (region) && ! complex_region)) { + mp_pipe->push (shape, world, 0, target); + } else { + insert_clipped (shape, region, complex_region, target); + } +} + +bool +ClippingHierarchyBuilderShapeReceiver::is_inside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) +{ + if (region == db::Box::world ()) { + return true; + } + + if (box.inside (region)) { + + db::Box rect_box = region & box; + + if (complex_region) { + + // TODO: this is not a real test for being inside a complex region + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert ()); ! cr.at_end (); ++cr) { + if (rect_box.inside (*cr)) { + return true; + } + } + + } + + } + + return false; +} + +bool +ClippingHierarchyBuilderShapeReceiver::is_outside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) +{ + if (region == db::Box::world ()) { + return false; + } + + if (box.overlaps (region)) { + + db::Box rect_box = region & box; + + if (complex_region) { + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert ()); ! cr.at_end (); ++cr) { + if (rect_box.overlaps (*cr)) { + return false; + } + } + } else { + return false; + } + + } + + return true; +} + +void +ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + db::Box bb = box & region; + static db::Box world = db::Box::world (); + + if (complex_region) { + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (bb, db::box_convert ()); ! cr.at_end (); ++cr) { + mp_pipe->push (*cr & bb, world, 0, target); + } + } else { + mp_pipe->push (bb, world, 0, target); + } +} + +void +ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Polygon &poly, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + std::vector clipped_poly; + static db::Box world = db::Box::world (); + + if (complex_region) { + // TODO: is this good way to clip a polygon at a complex boundary? + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (region, db::box_convert ()); ! cr.at_end (); ++cr) { + db::clip_poly (poly, *cr & region, clipped_poly); + } + } else { + db::clip_poly (poly, region, clipped_poly); + } + + for (std::vector::const_iterator p = clipped_poly.begin (); p != clipped_poly.end (); ++p) { + mp_pipe->push (*p, world, 0, target); + } +} + +// --------------------------------------------------------------------------------------------- + +ReducingHierarchyBuilderShapeReceiver::ReducingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe, double area_ratio, size_t max_vertex_count) + : mp_pipe (pipe ? pipe : &def_inserter), m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count) +{ + // .. nothing yet .. +} + +void +ReducingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + if (shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { + mp_pipe->push (shape, region, complex_region, target); + } else if (shape.is_box ()) { + mp_pipe->push (shape.box (), region, complex_region, target); + } else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { + db::Polygon poly; + shape.polygon (poly); + reduce (poly, region, complex_region, target); + } +} + +void +ReducingHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + mp_pipe->push (shape, region, complex_region, target); +} + +void +ReducingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + reduce (shape, region, complex_region, target); +} + +static double area_ratio (const db::Polygon &poly) +{ + return double (poly.box ().area ()) / double (poly.area ()); +} + +void +ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + size_t npoints = 0; + for (unsigned int c = 0; c < poly.holes () + 1; ++c) { + npoints += poly.contour (c).size (); + } + + if (npoints > m_max_vertex_count || area_ratio (poly) > m_area_ratio) { + + std::vector split_polygons; + db::split_polygon (poly, split_polygons); + for (std::vector ::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) { + reduce (*sp, region, complex_region, target); + } + + } else { + mp_pipe->push (poly, region, complex_region, target); + } +} + +// --------------------------------------------------------------------------------------------- + +PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout) + : mp_layout (layout) +{ + // nothing yet .. +} + +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + if (shape.is_box () || shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { + db::Polygon poly; + shape.polygon (poly); + target->insert (db::PolygonRef (poly, mp_layout->shape_repository ())); + } +} + +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + target->insert (db::PolygonRef (db::Polygon (shape), mp_layout->shape_repository ())); +} + +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + target->insert (db::PolygonRef (shape, mp_layout->shape_repository ())); +} + } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index c452beb25..c982ec384 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -43,6 +43,108 @@ namespace db */ int compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2); +/** + * @brief A class to receive shapes from the hierarchy builder + * + * This class can be reimplemented to implement clipping and/or + * simplification. + */ +class DB_PUBLIC HierarchyBuilderShapeReceiver +{ +public: + HierarchyBuilderShapeReceiver () { } + virtual ~HierarchyBuilderShapeReceiver () { } + + virtual void push (const db::Shape &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; + virtual void push (const db::Box &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; + virtual void push (const db::Polygon &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; +}; + +/** + * @brief A shape receiver that simply pushes into the target + */ +class DB_PUBLIC HierarchyBuilderShapeInserter + : public HierarchyBuilderShapeReceiver +{ +public: + HierarchyBuilderShapeInserter () { } + + virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + { + target->insert (shape); + } + + virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + { + target->insert (shape); + } + + virtual void push (const db::Polygon &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + { + target->insert (shape); + } +}; + +/** + * @brief A clipping shape receiver that forwards to another one + */ +class DB_PUBLIC ClippingHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + ClippingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe = 0); + + virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + +private: + void insert_clipped (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + void insert_clipped (const db::Polygon &poly, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + static bool is_inside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region); + static bool is_outside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region); + + HierarchyBuilderShapeReceiver *mp_pipe; +}; + +/** + * @brief A polygon reducing shape receiver that forwards to another one + */ +class DB_PUBLIC ReducingHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + ReducingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe = 0, double area_ratio = 3.0, size_t max_vertex_count = 16); + + virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + +private: + void reduce (const db::Polygon &poly, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + + HierarchyBuilderShapeReceiver *mp_pipe; + double m_area_ratio; + size_t m_max_vertex_count; +}; + +/** + * @brief A polygon reference generating shape receiver that feeds a shapes array after turning the shapes into PolygonRefs + */ +class DB_PUBLIC PolygonReferenceHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout); + + virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + +private: + db::Layout *mp_layout; +}; + /** * @brief A class building a hierarchy from a recursive shape iterator in push mode * @@ -58,26 +160,55 @@ int compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShape * rely on precisely the same hierarchy arrangement. This is not given with * region selections. */ -class HierarchyBuilder +class DB_PUBLIC HierarchyBuilder : public db::RecursiveShapeReceiver { public: typedef std::map >, db::cell_index_type> cell_map_type; - HierarchyBuilder (db::Layout *target, unsigned int target_layer, bool clip_shapes); + HierarchyBuilder (db::Layout *target, unsigned int target_layer, HierarchyBuilderShapeReceiver *pipe = 0); + HierarchyBuilder (db::Layout *target, HierarchyBuilderShapeReceiver *pipe = 0); + virtual ~HierarchyBuilder (); + + /** + * @brief Installs a custom shape receiver + * The hierarchy builder will *NOT* take ownership of this object. + */ + void set_shape_receiver (HierarchyBuilderShapeReceiver *pipe); virtual void begin (const RecursiveShapeIterator *iter); - virtual void end (const RecursiveShapeIterator * /*iter*/); - virtual void enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/); - virtual void leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/); - virtual bool new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all); - virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); - virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &trans); + virtual void end (const RecursiveShapeIterator *iter); + virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region); + virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell); + virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); + + /** + * @brief Sets the target layer - shapes will be put there + */ + void set_target_layer (unsigned int target_layer) + { + m_target_layer = target_layer; + } + + /** + * @brief Reset the builder - performs a new initial pass + */ + void reset (); + + /** + * @brief Gets the initial cell the builder produced + */ + db::Cell *initial_cell () + { + return mp_initial_cell; + } private: tl::weak_ptr mp_target; - bool m_clip_shapes; + HierarchyBuilderShapeReceiver *mp_pipe; bool m_initial_pass; db::RecursiveShapeIterator m_ref_iter; cell_map_type m_cell_map; @@ -85,6 +216,7 @@ private: cell_map_type::const_iterator m_cm_entry; unsigned int m_target_layer; std::vector m_cell_stack; + db::Cell *mp_initial_cell; }; } diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 90e2a9200..0380deb12 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1511,6 +1511,24 @@ Layout::insert_layer (unsigned int index, const LayerProperties &props) layer_properties_changed (); } +unsigned int +Layout::get_layer (const db::LayerProperties &lp) +{ + if (lp.is_null ()) { + // for a null layer info always create a layer + return insert_layer (); + } else { + // if we have a layer with the requested properties already, return this. + for (db::Layout::layer_iterator li = begin_layers (); li != end_layers (); ++li) { + if ((*li).second->log_equal (lp)) { + return (*li).first; + } + } + // otherwise create a new layer + return insert_layer (lp); + } +} + unsigned int Layout::waste_layer () const { diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index e1724657d..8ebaf1fbc 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -1447,6 +1447,14 @@ public: */ void insert_layer (unsigned int index, const LayerProperties &props = LayerProperties ()); + /** + * @brief Gets or creates a layer with the given properties + * + * If there already is a layer matching the given properties, it's index will be + * returned. Otherwise a new layer with these properties is created. + */ + unsigned int get_layer (const db::LayerProperties &props); + /** * @brief Insert a new special layer with the given properties * diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 2c9216776..d55bce34c 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -716,8 +716,10 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const m_inst_quad_id_stack.push_back (m_inst_quad_id); bool ia = is_inactive (); + bool aoi = is_all_of_instance (); mp_cell = &mp_layout->cell (m_inst->cell_index ()); set_inactive (ia); + set_all_of_instance (aoi); m_trans = m_trans * m_inst->complex_trans (*m_inst_array); @@ -876,22 +878,34 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const } } + bool all_of_instance = false; + bool with_region = false; + if (m_local_region_stack.back () != box_type::world () && ! m_inst->cell_inst ().bbox (m_box_convert).inside (m_local_region_stack.back ())) { - if (receiver && ! receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), false)) { - m_inst_array = inst_array_iterator (); - } else { - m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); - } + with_region = true; } else { // TODO: optimization potential: only report all_of_instance == false, if not entirely within the complex region - bool all_of_instance = m_local_complex_region_stack.empty (); - if (receiver && ! receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance)) { - m_inst_array = inst_array_iterator (); - } else { - m_inst_array = m_inst->cell_inst ().begin (); - } + all_of_instance = m_local_complex_region_stack.empty (); } + RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all; + if (receiver) { + ni = receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + } + + if (ni == RecursiveShapeReceiver::NI_skip) { + m_inst_array = inst_array_iterator (); + } else if (ni == RecursiveShapeReceiver::NI_single) { + // a singular iterator + m_inst_array = db::CellInstArray::iterator (m_inst->cell_inst ().front (), false); + } else if (with_region) { + m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + } else { + m_inst_array = m_inst->cell_inst ().begin (); + } + + set_all_of_instance (all_of_instance); + new_inst_member (receiver); if (! m_inst_array.at_end ()) { @@ -921,7 +935,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const } while (! m_inst_array.at_end () && receiver) { - if (receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ())) { + if (receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) { break; } else { ++m_inst_array; @@ -950,7 +964,7 @@ RecursiveShapeIterator::push (RecursiveShapeReceiver *receiver) validate (receiver); while (! at_end ()) { - receiver->shape (this, *m_shape, m_trans); + receiver->shape (this, *m_shape, m_trans, m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); next (receiver); } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index ce54b7c2f..59cc5679f 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -548,7 +548,7 @@ public: } /** - * @brief Get the current depth + * @brief Gets the current depth * * Returns the number of hierarchy levels we are below top level currently. */ @@ -559,7 +559,7 @@ public: } /** - * @brief Get the current shape + * @brief Gets the current shape * * Returns the shape currently referred to by the recursive iterator. * This shape is not transformed yet and is located in the current cell. @@ -600,27 +600,25 @@ public: bool at_end () const; /** - * @brief Get the current cell's index + * @brief Gets the current cell's index */ db::cell_index_type cell_index () const { - validate (0); - size_t c = reinterpret_cast (mp_cell); - return reinterpret_cast (c - (c & size_t (1)))->cell_index (); + return cell ()->cell_index (); } /** - * @brief Get the current cell's reference + * @brief Gets the current cell's reference */ const cell_type *cell () const { validate (0); size_t c = reinterpret_cast (mp_cell); - return reinterpret_cast (c - (c & size_t (1))); + return reinterpret_cast (c - (c & size_t (3))); } /** - * @brief Increment the iterator (operator version) + * @brief Increments the iterator (operator version) */ RecursiveShapeIterator &operator++() { @@ -749,7 +747,19 @@ private: { size_t c = reinterpret_cast (mp_cell); c -= (c & size_t (1)); - mp_cell = reinterpret_cast (c + a); + mp_cell = reinterpret_cast (c + (a ? 1 : 0)); + } + + bool is_all_of_instance () const + { + return (reinterpret_cast (mp_cell) & size_t (2)) != 0; + } + + void set_all_of_instance (bool a) const + { + size_t c = reinterpret_cast (mp_cell); + c -= (c & size_t (2)); + mp_cell = reinterpret_cast (c + (a ? 2 : 0)); } }; @@ -769,7 +779,19 @@ class DB_PUBLIC RecursiveShapeReceiver public: typedef RecursiveShapeIterator::box_tree_type box_tree_type; + /** + * @brief See new_inst for details. + */ + enum new_inst_mode { NI_all = 0, NI_single = 1, NI_skip = 2 }; + + /** + * @brief Constructor + */ RecursiveShapeReceiver () { } + + /** + * @brief Destructor + */ virtual ~RecursiveShapeReceiver () { } /** @@ -824,9 +846,12 @@ public: * * The "all" parameter is true, if all instances of the array will be addressed. * - * If this method returns false, the instance (the whole array) is skipped and the cell is not entered. + * This method can return the following values: + * - NI_all: iterate all members through "new_inst_member" + * - NI_single: iterate a single member (the first one) + * - NI_skip: skips the whole array (not a single instance is iterated) */ - virtual bool new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } + virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; } /** * @brief Enters a new array member of the instance @@ -835,16 +860,18 @@ public: * which holds the complex transformation for this particular instance of * the array. * + * "all" is true, if an instance array is iterated in "all" mode (see new_inst). + * * If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered. */ - virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { return true; } + virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } /** * @brief Delivers a shape * * @param trans The transformation which maps the shape to the top cell. */ - virtual void shape (const RecursiveShapeIterator * /*iter*/, const db::Shape & /*shape*/, const db::ICplxTrans & /*trans*/) { } + virtual void shape (const RecursiveShapeIterator * /*iter*/, const db::Shape & /*shape*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } }; } // namespace db diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 298655b8f..48c60db98 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -367,41 +367,24 @@ static std::vector multi_clip_into (db::Layout *l, db::cell return db::clip_layout(*l, *t, c, boxes, true); } -static unsigned int get_layer (db::Layout *l, const db::LayerProperties &lp) -{ - if (lp.is_null ()) { - // for a null layer info always create a layer - return l->insert_layer (); - } else { - // if we have a layer with the requested properties already, return this. - for (db::Layout::layer_iterator li = l->begin_layers (); li != l->end_layers (); ++li) { - if ((*li).second->log_equal (lp)) { - return (*li).first; - } - } - // otherwise create a new layer - return l->insert_layer (lp); - } -} - static unsigned int get_layer0 (db::Layout *l) { - return get_layer (l, db::LayerProperties ()); + return l->get_layer (db::LayerProperties ()); } static unsigned int get_layer1 (db::Layout *l, const std::string &name) { - return get_layer (l, db::LayerProperties (name)); + return l->get_layer (db::LayerProperties (name)); } static unsigned int get_layer2 (db::Layout *l, int ln, int dn) { - return get_layer (l, db::LayerProperties (ln, dn)); + return l->get_layer (db::LayerProperties (ln, dn)); } static unsigned int get_layer3 (db::Layout *l, int ln, int dn, const std::string &name) { - return get_layer (l, db::LayerProperties (ln, dn, name)); + return l->get_layer (db::LayerProperties (ln, dn, name)); } static tl::Variant find_layer (db::Layout *l, const db::LayerProperties &lp) @@ -1297,7 +1280,7 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.25.\n" ) + - gsi::method_ext ("layer", &get_layer, + gsi::method ("layer", &db::Layout::get_layer, "@brief Finds or creates a layer with the given properties\n" "@args info\n" "\n" diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc new file mode 100644 index 000000000..1dffa2d9d --- /dev/null +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -0,0 +1,337 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbHierarchyBuilder.h" +#include "dbReader.h" +#include "dbTestSupport.h" +#include "dbRegion.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target, false); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + + iter.push (&builder); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au1.gds"); +} + +TEST(2_WithoutClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target, false); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2a.gds"); +} + +TEST(2_WithClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ClippingHierarchyBuilderShapeReceiver clip; + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2b.gds"); +} + +TEST(2_WithClipAndSimplification) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ReducingHierarchyBuilderShapeReceiver red(0, 1.2, 4); + db::ClippingHierarchyBuilderShapeReceiver clip(&red); + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2c.gds"); +} + +TEST(2_WithClipAndRefGeneration) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::PolygonReferenceHierarchyBuilderShapeReceiver ref(&target); + db::ClippingHierarchyBuilderShapeReceiver clip(&ref); + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2d.gds"); +} + +TEST(2_WithEmptyResult) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::PolygonReferenceHierarchyBuilderShapeReceiver ref(&target); + db::ClippingHierarchyBuilderShapeReceiver clip(&ref); + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, 10000, 18500, 15000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2e.gds"); +} + +TEST(3_ComplexRegionWithClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ClippingHierarchyBuilderShapeReceiver clip; + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Region reg; + reg.insert (db::Box (5000, 13000, 18500, 20000)); + reg.insert (db::Box (11000, 20000, 18500, 36000)); + reg.merge (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, reg); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au3a.gds"); +} + +TEST(4_ComplexRegionAndLayoutWithClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l3.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ClippingHierarchyBuilderShapeReceiver clip; + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Region reg; + reg.insert (db::Box (5000, 13000, 18500, 20000)); + reg.insert (db::Box (11000, 20000, 18500, 36000)); + reg.merge (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, reg); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au4a.gds"); +} + diff --git a/src/db/unit_tests/dbRecursiveShapeIterator.cc b/src/db/unit_tests/dbRecursiveShapeIterator.cc index 03dc7f918..dfd115c27 100644 --- a/src/db/unit_tests/dbRecursiveShapeIterator.cc +++ b/src/db/unit_tests/dbRecursiveShapeIterator.cc @@ -918,19 +918,23 @@ public: m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; } - virtual bool new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) { m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); if (all) { m_text += ",all"; } m_text += ")\n"; - return true; + return NI_all; } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) { - m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (trans) + ")\n"; + m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (trans); + if (all) { + m_text += ",all"; + } + m_text += ")\n"; return true; } @@ -949,10 +953,26 @@ class ReceiverRejectingACellInstanceArray public: ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { } - virtual bool new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) { LoggingReceiver::new_inst (iter, inst, region, complex_region, all); - return inst.object ().cell_index () != m_rejected; + return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip; + } + +private: + db::cell_index_type m_rejected; +}; + +class ReceiverRejectingACellInstanceArrayExceptOne + : public LoggingReceiver +{ +public: + ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { } + + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) + { + LoggingReceiver::new_inst (iter, inst, region, complex_region, all); + return inst.object ().cell_index () != m_rejected ? NI_all : NI_single; } private: @@ -965,9 +985,9 @@ class ReceiverRejectingACellInstance public: ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) { - LoggingReceiver::new_inst_member (iter, inst, trans, region, complex_region); + LoggingReceiver::new_inst_member (iter, inst, trans, region, complex_region, all); return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected; } @@ -1001,82 +1021,82 @@ TEST(10) EXPECT_EQ (lr1.text (), "begin\n" "new_inst($2,all)\n" - "new_inst_member($2,r0 *1 0,0)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 0,6000)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,6000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,0)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,0)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,6000)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,6000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" "leave_cell($3)\n" @@ -1091,25 +1111,67 @@ TEST(10) EXPECT_EQ (rr1.text (), "begin\n" "new_inst($2,all)\n" - "new_inst_member($2,r0 *1 0,0)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 0,6000)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,0)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,6000)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "leave_cell($2)\n" "end\n" ); + ReceiverRejectingACellInstanceArrayExceptOne rs1 (c2.cell_index ()); + db::RecursiveShapeIterator is1 (g, c0, 0); + is1.push (&rs1); + + EXPECT_EQ (rs1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,6000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,0)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,6000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + ReceiverRejectingACellInstance rri1 (c2.cell_index (), db::ICplxTrans ()); db::RecursiveShapeIterator iri1 (g, c0, 0); iri1.push (&rri1); @@ -1117,70 +1179,70 @@ TEST(10) EXPECT_EQ (rri1.text (), "begin\n" "new_inst($2,all)\n" - "new_inst_member($2,r0 *1 0,0)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" // -> skipped - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" // -> skipped + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 0,6000)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,0)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,6000)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" "leave_cell($3)\n" diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index c1a034f6a..2e3f8a0e1 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -53,7 +53,8 @@ SOURCES = \ dbWriterTools.cc \ dbVariableWidthPath.cc \ dbLoadLayoutOptionsTests.cc \ - dbSaveLayoutOptionsTests.cc + dbSaveLayoutOptionsTests.cc \ + dbHierarchyBuilderTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/tl/tl/tlObject.cc b/src/tl/tl/tlObject.cc index 0d1b4cfca..305e0b3bb 100644 --- a/src/tl/tl/tlObject.cc +++ b/src/tl/tl/tlObject.cc @@ -130,12 +130,12 @@ bool Object::has_strong_references () const return false; } -void Object::keep () +void Object::keep_object () { mp_ptrs = (WeakOrSharedPtr *)(size_t (mp_ptrs) | size_t (1)); } -void Object::release () +void Object::release_object () { mp_ptrs = (WeakOrSharedPtr *)(size_t (mp_ptrs) & ~size_t (1)); diff --git a/src/tl/tl/tlObject.h b/src/tl/tl/tlObject.h index df78b0435..a66426675 100644 --- a/src/tl/tl/tlObject.h +++ b/src/tl/tl/tlObject.h @@ -106,14 +106,14 @@ public: * no strong pointer is having a reference to this object and longer, the * object is not deleted. */ - void keep (); + void keep_object (); /** * @brief Releases this object from being kept * This method may delete the object if no strong pointer holds a * reference to it. */ - void release (); + void release_object (); protected: /** diff --git a/testdata/algo/hierarchy_builder_au1.gds b/testdata/algo/hierarchy_builder_au1.gds new file mode 100644 index 0000000000000000000000000000000000000000..fa837f4e2a317771de909276f480b397e7cf8199 GIT binary patch literal 2762 zcma);(MuFj6vofa&fb|_S2L5skSHmrP>VDpBvIQ+F*MvwY7z9Ymmo+YqCzOak|3xj z>p_C-!M7j~^eH03ryheM_8$lqd~(U1zB6}@Gb7HJpugpu^WAgrJ?Ea46dA@1YEBqd zi89no2k0cV1wWH*sH=nhM2*Q*Pv5h1Ltj6X&*zp;Exaifs5up_%P3VC?HkS$nKqHC zHOL@UXAn_qebR9@?>SEO7?UXRlgM~36g!j5y}sTI>Z=C)Yxo77Mew&zu~*mP^w>#} z^~l@BdY@vaVw4`SEzqoKI*VYH^J|JdIzQ_;zhzDNMt0P=RdruO7E7VxeB**z%ViF|}>#P>V z_J^(1;PBWPGBZS(2I-Y7_eS-OyO3M^oxPc|&JpsegNbP(tbmiej96h?jI86GP;Mx2 z!o)4^SNBvz-_VScSlOe6#>Qz$n4XHdZ`@8R&Na>?N>8C~uZ3b~#^_7P)v0gUe5>mv z6nnBWq?dZ>_qL86QOLWF(($|bBNRI`N{<`e=lVg`A%Cq~Z_w z7)KKp9EWLf@1sYAVpoOG{_Wsb_9y%s4&j)voo~k6JGjd%8WO zPlWSxpJ;vE53Lu99eos>cPU?wy|&=~9%!0lAm)Wo>|lf*_x29=H?L`7_EsKaKOV)d z=0keR&yU(Z!TqdgI-gL-NBHbf?A4VxJv2kC$KEfo-ly1UC|;jl()!GMtrvDKz}S*;g}y{231vs?3Szd;F}DEi%@KB@^*D-?y9AyHe?z;03_*9#~Re|zmR2k|{Q zIyih~1d*6#c7vXZ?i9l=`A6np$d|bm?Q2|!_8lrm`%d&m`#QoCHMExtrD)y2>1f~R zbhK~$yYIu8<^LaKI=l01`#h+8(er@$Kl5N?0CBx|51@ryMa%=C*j2B-doO=ro)AqO zXT2utCYQCIDfVsp(0kf=g}6E72dx>r#}_`uuI57d1A3yH9jw>1X0ZP@)@zDAPCs#1 z>zS;ZyzP_qp5N^~?>*rBey2~A5pNTBdj6uI} literal 0 HcmV?d00001 diff --git a/testdata/algo/hierarchy_builder_au2a.gds b/testdata/algo/hierarchy_builder_au2a.gds new file mode 100644 index 0000000000000000000000000000000000000000..9d556aed68022a0a7ef97c024d1f7caf7f78fbac GIT binary patch literal 11526 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/hierarchy_builder_au2c.gds b/testdata/algo/hierarchy_builder_au2c.gds new file mode 100644 index 0000000000000000000000000000000000000000..85f5d60828ff1e721f426cbfab24f4ec7f4d4abb GIT binary patch literal 11902 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/hierarchy_builder_au3a.gds b/testdata/algo/hierarchy_builder_au3a.gds new file mode 100644 index 0000000000000000000000000000000000000000..671fad2a127b57e495b462d239b216fad506a449 GIT binary patch literal 1172 zcmZQzV_;&6V31*CVt>TI&A`tf#vsoihs^+>@@d2w)}&o%MSeo zv!g;7WLR@*qxI}NehDck|^k8lSA z1N#MtKOr1Dg~hFs1QGvpNbuQXB)1466f_$-o0jn;@X~0Ad@^3=woQR1AqV z#Vr7jDMrMa5_lm5g`l= LWLSK%urL4sAoi>t literal 0 HcmV?d00001 diff --git a/testdata/algo/hierarchy_builder_au4a.gds b/testdata/algo/hierarchy_builder_au4a.gds new file mode 100644 index 0000000000000000000000000000000000000000..504df9920758aaa48972b806e6876bb7b4ac31b5 GIT binary patch literal 82618 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/hierarchy_builder_l2.gds b/testdata/algo/hierarchy_builder_l2.gds new file mode 100644 index 0000000000000000000000000000000000000000..7447367b20bad86fd70c5bed764beb464751061a GIT binary patch literal 268 zcmZQzV_;&6V31*CVt>TI&A`tf#=yfMhRkN*U}E#}bYfr-VP>^+>@@d2w)}&o%MSeo zv!g;7WLRhA^7M1}XJBCAU|?Y5Wny4tVB=$AU|`S@VE+IAm&*VDeh3tMA;O~z{1VIz$FB;mqQq2Cl`Z^05i~5EZ|iF+D6vofa&fb~b^0dsNTOz?7#i*-C4&3dOAsUxQ6ZFINf6YN z^&mm^;9C#~`V+Y@DKd;})R-`= zUzDY4+D&KZaPT|XhPvF}LsXwkb#^~FJ@ENm@oewn@wr!{d1_2W>oQ8^hr0)JM5axo zDh)D7RT)IoT$6O1^*fGJKExzSd?zxV3dPPObC<6-gZi=o{|Y{!GY|gsDfaS8oF0{o zvL1QcS?^QqG#aHxY!fsqn$A2}=KPvskIv6J&aathXB|Es#l9)O3hsY>8TlXM{Fb4+ zkK6}^VrPcx59pEmhwBGfhx6|hirv?z_5CHi{OSPTqFtTD?-JG5Fv)TLtul#{`*l`} zV*A5Zs(*0gB$*kaOs(`v1@}hz7EDFGO317BCMJmxgIb@v$=v>ki`jCWjVxHhUBTO- zoKRqgi5uLn?yiWwAiMyLM+=RO-I6dp5p~`;oo4K7j7gN9z`eZ`ik%swUq!Aqeahx* zeP2ScC)+}Lxi8d@e1Fj+iixY^ck@Rmc4m|w=UwOeLDnIErBLkgdhRpS=k}Y_EfjlP z{cB@r!o1@!E$)5vh*0b*AFAIDzL(N8G@Xu9n#Nu}5~c16#m}ocoxBUFL+ebK`B~9l8?(qS>dlY+lDNYZ~0P9iv1=jl%I}OC^(+gUkd8_q8v8NZ} z_1O-suQ;XkLa}E%w7z0v-t93c!ShDHJKT?Iz|;ywVP;6w#x$@S)X>$uH(R!ugZMi+ z+&_3}2+^2hcCDU@$cY;DkIcA`FLT9pZt1I^i}vj+M*EI-Mf+OA^EOn=xz%W0--&47 z@MN@a?5pp?nC1T;WIDU^Z1X%QebV!Qxs!RY)`z$*ya&)iu2IYbq1aVdsGkD!glPN- z>or+dSk!u^*f;4z?`iD?;%1N^w5ISLpZOHKnhE6(=!vekvR>1gLjA{BuPOF8{rGLI zXR@wv(+SX8cKE(>WM2bQ>IEI literal 0 HcmV?d00001 From 04256a275307c729afc8b5c1463913f417d45e84 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 15 Nov 2018 22:59:38 +0100 Subject: [PATCH 054/335] WIP: fixed RecursiveShapeIterator unit tests. --- ...rsiveShapeIterator.cc => dbRecursiveShapeIteratorTests.cc} | 4 ++-- src/db/unit_tests/unit_tests.pro | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/db/unit_tests/{dbRecursiveShapeIterator.cc => dbRecursiveShapeIteratorTests.cc} (99%) diff --git a/src/db/unit_tests/dbRecursiveShapeIterator.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc similarity index 99% rename from src/db/unit_tests/dbRecursiveShapeIterator.cc rename to src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index dfd115c27..18d308d82 100644 --- a/src/db/unit_tests/dbRecursiveShapeIterator.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -715,7 +715,7 @@ class FlatPusher public: FlatPusher (std::set *boxes) : mp_boxes (boxes) { } - void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans) + void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { mp_boxes->insert (trans * shape.bbox ()); } @@ -938,7 +938,7 @@ public: return true; } - virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans) + virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { m_text += "shape(" + shape.to_string () + "," + tl::to_string (trans) + ")\n"; } diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 2e3f8a0e1..07b2e6225 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -39,7 +39,6 @@ SOURCES = \ dbPolygon.cc \ dbPolygonTools.cc \ dbPropertiesRepository.cc \ - dbRecursiveShapeIterator.cc \ dbRegion.cc \ dbShapeArray.cc \ dbShape.cc \ @@ -54,7 +53,8 @@ SOURCES = \ dbVariableWidthPath.cc \ dbLoadLayoutOptionsTests.cc \ dbSaveLayoutOptionsTests.cc \ - dbHierarchyBuilderTests.cc + dbHierarchyBuilderTests.cc \ + dbRecursiveShapeIteratorTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC From f29fd3dfc6d9267f3e5b98ef2ccdf07445cdd4af Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 15 Nov 2018 23:41:44 +0100 Subject: [PATCH 055/335] WIP: moved hierarchical processor into db. --- src/db/db/db.pro | 8 +- .../db_plugin => db/db}/dbHierProcessor.cc | 0 .../db_plugin => db/db}/dbHierProcessor.h | 25 +-- .../db_plugin => db/db}/dbLocalOperation.cc | 0 .../db_plugin => db/db}/dbLocalOperation.h | 9 +- .../unit_tests/dbHierProcessorTests.cc | 2 +- src/db/unit_tests/unit_tests.pro | 3 +- .../tools/netx/db_plugin/dbNetExtractor.cc | 177 ------------------ .../tools/netx/db_plugin/dbNetExtractor.h | 120 ------------ .../netx/db_plugin/dbNetExtractorPlugin.cc | 29 --- .../tools/netx/db_plugin/db_plugin.pro | 18 -- .../netx/db_plugin/gsiDeclDbNetExtractor.cc | 90 --------- src/plugins/tools/netx/netx.pro | 11 -- .../netx/unit_tests/dbNetExtractorTests.cc | 30 --- .../tools/netx/unit_tests/unit_tests.pro | 21 --- .../netx/testdata => testdata/algo}/hlp1.oas | Bin .../netx/testdata => testdata/algo}/hlp10.oas | Bin .../netx/testdata => testdata/algo}/hlp11.oas | Bin .../netx/testdata => testdata/algo}/hlp2.oas | Bin .../netx/testdata => testdata/algo}/hlp3.oas | Bin .../netx/testdata => testdata/algo}/hlp4.oas | Bin .../netx/testdata => testdata/algo}/hlp5.oas | Bin .../netx/testdata => testdata/algo}/hlp6.oas | Bin .../netx/testdata => testdata/algo}/hlp7.oas | Bin .../netx/testdata => testdata/algo}/hlp8.oas | Bin .../netx/testdata => testdata/algo}/hlp9.oas | Bin 26 files changed, 27 insertions(+), 516 deletions(-) rename src/{plugins/tools/netx/db_plugin => db/db}/dbHierProcessor.cc (100%) rename src/{plugins/tools/netx/db_plugin => db/db}/dbHierProcessor.h (95%) rename src/{plugins/tools/netx/db_plugin => db/db}/dbLocalOperation.cc (100%) rename src/{plugins/tools/netx/db_plugin => db/db}/dbLocalOperation.h (95%) rename src/{plugins/tools/netx => db}/unit_tests/dbHierProcessorTests.cc (99%) delete mode 100644 src/plugins/tools/netx/db_plugin/dbNetExtractor.cc delete mode 100644 src/plugins/tools/netx/db_plugin/dbNetExtractor.h delete mode 100644 src/plugins/tools/netx/db_plugin/dbNetExtractorPlugin.cc delete mode 100644 src/plugins/tools/netx/db_plugin/db_plugin.pro delete mode 100644 src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc delete mode 100644 src/plugins/tools/netx/netx.pro delete mode 100644 src/plugins/tools/netx/unit_tests/dbNetExtractorTests.cc delete mode 100644 src/plugins/tools/netx/unit_tests/unit_tests.pro rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp1.oas (100%) rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp10.oas (100%) rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp11.oas (100%) rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp2.oas (100%) rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp3.oas (100%) rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp4.oas (100%) rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp5.oas (100%) rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp6.oas (100%) rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp7.oas (100%) rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp8.oas (100%) rename {src/plugins/tools/netx/testdata => testdata/algo}/hlp9.oas (100%) diff --git a/src/db/db/db.pro b/src/db/db/db.pro index f32306b99..c0b0d45c6 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -137,7 +137,9 @@ SOURCES = \ dbOriginalLayerEdgePairs.cc \ dbEdgePairsDelegate.cc \ dbDeepShapeStore.cc \ - dbHierarchyBuilder.cc + dbHierarchyBuilder.cc \ + dbLocalOperation.cc \ + dbHierProcessor.cc HEADERS = \ dbArray.h \ @@ -244,7 +246,9 @@ HEADERS = \ dbOriginalLayerEdgePairs.h \ dbEdgePairsDelegate.h \ dbDeepShapeStore.h \ - dbHierarchyBuilder.h + dbHierarchyBuilder.h \ + dbLocalOperation.h \ + dbHierProcessor.h !equals(HAVE_QT, "0") { diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc similarity index 100% rename from src/plugins/tools/netx/db_plugin/dbHierProcessor.cc rename to src/db/db/dbHierProcessor.cc diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/db/db/dbHierProcessor.h similarity index 95% rename from src/plugins/tools/netx/db_plugin/dbHierProcessor.h rename to src/db/db/dbHierProcessor.h index 307d32569..71128dec4 100644 --- a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -25,8 +25,9 @@ #ifndef HDR_dbHierProcessor #define HDR_dbHierProcessor +#include "dbCommon.h" + #include "dbLayout.h" -#include "dbPluginCommon.h" #include "dbLocalOperation.h" #include "tlThreadedWorkers.h" @@ -36,9 +37,9 @@ #include #include -// @@@ should go into dbHash.h #include "dbHash.h" +// @@@ should go into dbHash.h namespace std { template @@ -82,7 +83,7 @@ class LocalProcessorCellContext; class LocalProcessorContexts; // TODO: move this somewhere else? -class DB_PLUGIN_PUBLIC ShapeInteractions +class DB_PUBLIC ShapeInteractions { public: typedef std::unordered_map > container; @@ -120,7 +121,7 @@ private: }; // TODO: should be hidden (private data?) -struct DB_PLUGIN_PUBLIC LocalProcessorCellDrop +struct DB_PUBLIC LocalProcessorCellDrop { LocalProcessorCellDrop (db::LocalProcessorCellContext *_parent_context, db::Cell *_parent, const db::ICplxTrans &_cell_inst) : parent_context (_parent_context), parent (_parent), cell_inst (_cell_inst) @@ -134,7 +135,7 @@ struct DB_PLUGIN_PUBLIC LocalProcessorCellDrop }; // TODO: should be hidden (private data?) -class DB_PLUGIN_PUBLIC LocalProcessorCellContext +class DB_PUBLIC LocalProcessorCellContext { public: typedef std::pair parent_inst_type; @@ -170,7 +171,7 @@ private: tl::Mutex m_lock; }; -class DB_PLUGIN_PUBLIC LocalProcessorCellContexts +class DB_PUBLIC LocalProcessorCellContexts { public: typedef std::pair, std::unordered_set > key_type; @@ -199,7 +200,7 @@ private: std::unordered_map m_contexts; }; -class DB_PLUGIN_PUBLIC LocalProcessorContexts +class DB_PUBLIC LocalProcessorContexts { public: typedef std::unordered_map contexts_per_cell_type; @@ -271,7 +272,7 @@ private: mutable tl::Mutex m_lock; }; -class DB_PLUGIN_PUBLIC LocalProcessorContextComputationTask +class DB_PUBLIC LocalProcessorContextComputationTask : public tl::Task { public: @@ -290,7 +291,7 @@ private: db::Coord m_dist; }; -class DB_PLUGIN_PUBLIC LocalProcessorContextComputationWorker +class DB_PUBLIC LocalProcessorContextComputationWorker : public tl::Worker { public: @@ -306,7 +307,7 @@ public: } }; -class DB_PLUGIN_PUBLIC LocalProcessorResultComputationTask +class DB_PUBLIC LocalProcessorResultComputationTask : public tl::Task { public: @@ -322,7 +323,7 @@ private: unsigned int m_output_layer; }; -class DB_PLUGIN_PUBLIC LocalProcessorResultComputationWorker +class DB_PUBLIC LocalProcessorResultComputationWorker : public tl::Worker { public: @@ -338,7 +339,7 @@ public: } }; -class DB_PLUGIN_PUBLIC LocalProcessor +class DB_PUBLIC LocalProcessor { public: LocalProcessor (db::Layout *layout, db::Cell *top); diff --git a/src/plugins/tools/netx/db_plugin/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc similarity index 100% rename from src/plugins/tools/netx/db_plugin/dbLocalOperation.cc rename to src/db/db/dbLocalOperation.cc diff --git a/src/plugins/tools/netx/db_plugin/dbLocalOperation.h b/src/db/db/dbLocalOperation.h similarity index 95% rename from src/plugins/tools/netx/db_plugin/dbLocalOperation.h rename to src/db/db/dbLocalOperation.h index 330d71441..8fe9ae0be 100644 --- a/src/plugins/tools/netx/db_plugin/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -25,8 +25,9 @@ #ifndef HDR_dbLocalOperation #define HDR_dbLocalOperation +#include "dbCommon.h" + #include "dbLayout.h" -#include "dbPluginCommon.h" #include #include @@ -48,7 +49,7 @@ class ShapeInteractions; * This class implements the actual operation. It receives a * cluster of subject shapes vs. corresponding intruder shapes. */ -class DB_PLUGIN_PUBLIC LocalOperation +class DB_PUBLIC LocalOperation { public: /** @@ -109,7 +110,7 @@ public: /** * @brief Implements a boolean AND or NOT operation */ -class DB_PLUGIN_PUBLIC BoolAndOrNotLocalOperation +class DB_PUBLIC BoolAndOrNotLocalOperation : public LocalOperation { public: @@ -128,7 +129,7 @@ private: * With a given wrap_count, the result will only contains shapes where * the original shapes overlap at least "wrap_count" times. */ -class DB_PLUGIN_PUBLIC SelfOverlapMergeLocalOperation +class DB_PUBLIC SelfOverlapMergeLocalOperation : public LocalOperation { public: diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc similarity index 99% rename from src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc rename to src/db/unit_tests/dbHierProcessorTests.cc index 94ac38232..3453aacd9 100644 --- a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -30,7 +30,7 @@ static std::string testdata (const std::string &fn) { - return tl::testsrc () + "/src/plugins/tools/netx/testdata/" + fn; + return tl::testsrc () + "/testdata/algo/" + fn; } enum TestMode diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 07b2e6225..e6b19cd84 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -54,7 +54,8 @@ SOURCES = \ dbLoadLayoutOptionsTests.cc \ dbSaveLayoutOptionsTests.cc \ dbHierarchyBuilderTests.cc \ - dbRecursiveShapeIteratorTests.cc + dbRecursiveShapeIteratorTests.cc \ + dbHierProcessorTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc deleted file mode 100644 index a8d7edfcb..000000000 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc +++ /dev/null @@ -1,177 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2018 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 "dbNetExtractor.h" -#include "dbHierProcessor.h" -#include "dbLayoutUtils.h" -#include "dbCellMapping.h" -#include "dbPolygonTools.h" -#include "dbBoxScanner.h" -#include "dbRecursiveShapeIterator.h" -#include "dbBoxConvert.h" -#include "tlLog.h" - -namespace db -{ - -NetExtractor::NetExtractor() - : mp_orig_layout (0), mp_layout (0), mp_top_cell (0), m_nthreads (0) -{ - - // @@@ - -} - -NetExtractor::~NetExtractor () -{ - delete mp_layout; - mp_layout = 0; - mp_top_cell = 0; -} - -void -NetExtractor::set_threads (unsigned int nthreads) -{ - m_nthreads = nthreads; -} - -void -NetExtractor::open (const db::Layout &orig_layout, cell_index_type orig_top_cell) -{ - tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Open layout"))); - - delete mp_layout; - mp_orig_layout = &orig_layout; - - mp_layout = new db::Layout (); - mp_layout->dbu (orig_layout.dbu ()); - mp_top_cell = &mp_layout->cell (mp_layout->add_cell (orig_layout.cell_name (orig_top_cell))); - - // copy hierarchy - m_cm.clear (); - m_cm.create_from_names_full (*mp_layout, mp_top_cell->cell_index (), orig_layout, orig_top_cell); -} - -void -NetExtractor::output (NetLayer a, const LayerProperties &lp) -{ - mp_layout->set_properties (a.layer_index (), lp); -} - -static double area_ratio (const db::Polygon &poly) -{ - return double (poly.box ().area ()) / double (poly.area ()); -} - -static void split_polygon_into (const db::Polygon &poly, db::Shapes &dest, size_t max_points, double max_area_ratio) -{ - size_t npoints = 0; - for (unsigned int c = 0; c < poly.holes () + 1; ++c) { - npoints += poly.contour (c).size (); - } - - if (npoints > max_points || area_ratio (poly) > max_area_ratio) { - - std::vector split_polygons; - db::split_polygon (poly, split_polygons); - for (std::vector ::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) { - split_polygon_into (*sp, dest, max_points, max_area_ratio); - } - - } else { - - dest.insert (db::PolygonRef (poly, dest.layout ()->shape_repository ())); - - } -} - -NetLayer -NetExtractor::load (unsigned int layer_index) -{ - tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Loading layer ")) + mp_orig_layout->get_properties (layer_index).to_string ()); - - const double max_area_ratio = 3.0; - const size_t max_points = 16; - - NetLayer lt (mp_layout->insert_layer ()); - mp_layout->set_properties (lt.layer_index(), mp_orig_layout->get_properties (layer_index)); - - for (db::Layout::const_iterator c = mp_orig_layout->begin (); c != mp_layout->end (); ++c) { - - if (m_cm.has_mapping (c->cell_index ())) { - - db::cell_index_type ct = m_cm.cell_mapping (c->cell_index ()); - - db::Shapes &dest_shapes = mp_layout->cell (ct).shapes (lt.layer_index()); - const db::Shapes &orig_shapes = c->shapes (layer_index); - for (db::Shapes::shape_iterator s = orig_shapes.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); ! s.at_end (); ++s) { - - // @@@ TODO: cache splitting and path to polygon conversion - db::Polygon poly; - s->polygon (poly); - - split_polygon_into (poly, dest_shapes, max_points, max_area_ratio); - - } - - } - - } - - return lt; -} - -NetLayer -NetExtractor::bool_and (NetLayer a, NetLayer b) -{ - return and_or_not (a, b, true); -} - -NetLayer -NetExtractor::bool_not (NetLayer a, NetLayer b) -{ - return and_or_not (a, b, false); -} - -NetLayer -NetExtractor::and_or_not (NetLayer a, NetLayer b, bool is_and) -{ - unsigned int lout = mp_layout->insert_layer (); - - db::BoolAndOrNotLocalOperation op (is_and); - db::LocalProcessor proc (mp_layout, mp_top_cell); - proc.set_threads (m_nthreads); - proc.run (&op, a.layer_index (), b.layer_index (), lout); - - return NetLayer (lout); -} - -db::Layout * -NetExtractor::layout_copy () const -{ - tl_assert (mp_layout != 0); - return new db::Layout (*mp_layout); -} - -} - diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.h b/src/plugins/tools/netx/db_plugin/dbNetExtractor.h deleted file mode 100644 index 979ec3a97..000000000 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.h +++ /dev/null @@ -1,120 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2018 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_dbNetExtractor -#define HDR_dbNetExtractor - -#include "dbPluginCommon.h" -#include "dbLayout.h" -#include "dbCellMapping.h" -#include "tlTypeTraits.h" - -namespace db -{ - -class NetLayer -{ -public: - NetLayer (unsigned int index) - : m_layer_index (index) - { - // .. nothing yet .. - } - - unsigned int layer_index () const - { - return m_layer_index; - } - -private: - unsigned int m_layer_index; -}; - -/** - * @brief The net extractor - * - * ... - */ -class DB_PLUGIN_PUBLIC NetExtractor -{ -public: - /** - * @brief Constructs a net extractor - */ - NetExtractor (); - - ~NetExtractor (); - - // @@@ - void open (const db::Layout &orig_layout, db::cell_index_type orig_top_cell); - NetLayer load (unsigned int layer_index); - NetLayer bool_and (NetLayer a, NetLayer b); - NetLayer bool_not (NetLayer a, NetLayer b); - void output (NetLayer a, const db::LayerProperties &lp); - db::Layout *layout_copy () const; - - void set_threads (unsigned int nthreads); - unsigned int threads () const - { - return m_nthreads; - } - -private: - // no copying - NetExtractor (const db::NetExtractor &); - NetExtractor &operator= (const db::NetExtractor &); - - NetLayer and_or_not (NetLayer a, NetLayer b, bool is_and); - - // @@@ - const db::Layout *mp_orig_layout; // @@@ should be a smart pointer - db::Layout *mp_layout; - db::Cell *mp_top_cell; - db::CellMapping m_cm; - unsigned int m_nthreads; -}; - -} - -namespace tl -{ - -template <> -struct type_traits : public tl::type_traits -{ - // mark "NetLayer" as not having a default ctor - typedef tl::false_tag has_default_constructor; -}; - -template <> -struct type_traits : public tl::type_traits -{ - // mark "NetExtractor" as not copyable - typedef tl::false_tag has_copy_constructor; -}; - -} - -#endif - diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractorPlugin.cc b/src/plugins/tools/netx/db_plugin/dbNetExtractorPlugin.cc deleted file mode 100644 index f06459c5b..000000000 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractorPlugin.cc +++ /dev/null @@ -1,29 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2018 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 - -*/ - - -namespace db -{ - - // @@@ - -} diff --git a/src/plugins/tools/netx/db_plugin/db_plugin.pro b/src/plugins/tools/netx/db_plugin/db_plugin.pro deleted file mode 100644 index 333cf6c44..000000000 --- a/src/plugins/tools/netx/db_plugin/db_plugin.pro +++ /dev/null @@ -1,18 +0,0 @@ - -TARGET = netx -DESTDIR = $$OUT_PWD/../../../../db_plugins - -include($$PWD/../../../db_plugin.pri) - -HEADERS = \ - dbNetExtractor.h \ - dbHierProcessor.h \ - dbLocalOperation.h - -SOURCES = \ - dbNetExtractor.cc \ - dbHierProcessor.cc \ - dbNetExtractorPlugin.cc \ - gsiDeclDbNetExtractor.cc \ - dbLocalOperation.cc - diff --git a/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc deleted file mode 100644 index 225b5bf1f..000000000 --- a/src/plugins/tools/netx/db_plugin/gsiDeclDbNetExtractor.cc +++ /dev/null @@ -1,90 +0,0 @@ -/* - - KLayout Layout Viewer - Copyright (C) 2006-2018 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 "dbNetExtractor.h" - -#include "gsiDecl.h" - -namespace gsi -{ - -gsi::Class decl_NetLayer ("db", "NetLayer", - gsi::method ("layer_index", &db::NetLayer::layer_index, - "@@@" - ), - "@brief The net extractor\n" - "\n" - "This class has been introduced in version 0.26." -); - -void open2 (db::NetExtractor *ex, const db::Layout *orig_layout, const db::Cell *cell) -{ - ex->open (*orig_layout, cell->cell_index ()); -} - -void output1 (db::NetExtractor *ex, const db::NetLayer &nl, const db::LayerProperties &lp) -{ - ex->output (nl, lp); -} - -void output2 (db::NetExtractor *ex, const db::NetLayer &nl, int layer, int datatype, const std::string &name) -{ - ex->output (nl, db::LayerProperties (layer, datatype, name)); -} - -gsi::Class decl_NetNetExtractor ("db", "NetExtractor", - gsi::method ("open", &db::NetExtractor::open, gsi::arg ("orig_layout"), gsi::arg ("orig_top_cell_index"), - "@@@" - ) + - gsi::method ("threads=", &db::NetExtractor::set_threads, gsi::arg ("nthreads"), - "@@@" - ) + - gsi::method ("threads", &db::NetExtractor::threads, - "@@@" - ) + - gsi::method_ext ("open", &open2, gsi::arg ("orig_layout"), gsi::arg ("orig_top_cell"), - "@@@" - ) + - gsi::method_ext ("output", &output1, gsi::arg ("net_layer"), gsi::arg ("layer"), - "@@@" - ) + - gsi::method_ext ("output", &output2, gsi::arg ("net_layer"), gsi::arg ("layer"), gsi::arg ("datatype"), gsi::arg ("name", std::string ()), - "@@@" - ) + - gsi::method ("load", &db::NetExtractor::load, gsi::arg ("layer_index"), - "@@@" - ) + - gsi::method ("bool_and", &db::NetExtractor::bool_and, gsi::arg ("a"), gsi::arg ("b"), - "@@@" - ) + - gsi::method ("bool_not", &db::NetExtractor::bool_not, gsi::arg ("a"), gsi::arg ("b"), - "@@@" - ) + - gsi::factory ("layout_copy", &db::NetExtractor::layout_copy, - "@@@" - ), - "@brief The net extractor\n" - "\n" - "This class has been introduced in version 0.26." -); - -} diff --git a/src/plugins/tools/netx/netx.pro b/src/plugins/tools/netx/netx.pro deleted file mode 100644 index f42b2a627..000000000 --- a/src/plugins/tools/netx/netx.pro +++ /dev/null @@ -1,11 +0,0 @@ - -TEMPLATE = subdirs - -SUBDIRS = db_plugin unit_tests -unit_tests.depends += db_plugin - -#!equals(HAVE_QT, "0") { -# SUBDIRS += lay_plugin -# lay_plugin.depends += db_plugin -#} - diff --git a/src/plugins/tools/netx/unit_tests/dbNetExtractorTests.cc b/src/plugins/tools/netx/unit_tests/dbNetExtractorTests.cc deleted file mode 100644 index 28ce60281..000000000 --- a/src/plugins/tools/netx/unit_tests/dbNetExtractorTests.cc +++ /dev/null @@ -1,30 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2018 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" - -TEST(1) -{ - // .. nothing yet .. -} - diff --git a/src/plugins/tools/netx/unit_tests/unit_tests.pro b/src/plugins/tools/netx/unit_tests/unit_tests.pro deleted file mode 100644 index a5e77b073..000000000 --- a/src/plugins/tools/netx/unit_tests/unit_tests.pro +++ /dev/null @@ -1,21 +0,0 @@ - -DESTDIR_UT = $$OUT_PWD/../../../.. - -TARGET = net_tracer_tests - -include($$PWD/../../../../lib_ut.pri) - -SOURCES = \ - dbNetExtractorTests.cc \ - dbHierProcessorTests.cc \ - -INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common -DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common - -LIBS += -L$$DESTDIR_UT -lklayout_db -lklayout_tl -lklayout_gsi - -# This makes the test pull the mebes library for testing (not installed) -PLUGINPATH = $$OUT_PWD/../../../../db_plugins -QMAKE_RPATHDIR += $$PLUGINPATH - -LIBS += -L$$PLUGINPATH -lnetx diff --git a/src/plugins/tools/netx/testdata/hlp1.oas b/testdata/algo/hlp1.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp1.oas rename to testdata/algo/hlp1.oas diff --git a/src/plugins/tools/netx/testdata/hlp10.oas b/testdata/algo/hlp10.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp10.oas rename to testdata/algo/hlp10.oas diff --git a/src/plugins/tools/netx/testdata/hlp11.oas b/testdata/algo/hlp11.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp11.oas rename to testdata/algo/hlp11.oas diff --git a/src/plugins/tools/netx/testdata/hlp2.oas b/testdata/algo/hlp2.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp2.oas rename to testdata/algo/hlp2.oas diff --git a/src/plugins/tools/netx/testdata/hlp3.oas b/testdata/algo/hlp3.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp3.oas rename to testdata/algo/hlp3.oas diff --git a/src/plugins/tools/netx/testdata/hlp4.oas b/testdata/algo/hlp4.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp4.oas rename to testdata/algo/hlp4.oas diff --git a/src/plugins/tools/netx/testdata/hlp5.oas b/testdata/algo/hlp5.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp5.oas rename to testdata/algo/hlp5.oas diff --git a/src/plugins/tools/netx/testdata/hlp6.oas b/testdata/algo/hlp6.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp6.oas rename to testdata/algo/hlp6.oas diff --git a/src/plugins/tools/netx/testdata/hlp7.oas b/testdata/algo/hlp7.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp7.oas rename to testdata/algo/hlp7.oas diff --git a/src/plugins/tools/netx/testdata/hlp8.oas b/testdata/algo/hlp8.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp8.oas rename to testdata/algo/hlp8.oas diff --git a/src/plugins/tools/netx/testdata/hlp9.oas b/testdata/algo/hlp9.oas similarity index 100% rename from src/plugins/tools/netx/testdata/hlp9.oas rename to testdata/algo/hlp9.oas From f5cc8b6018d75bd50dfc63072f92090d2a9b9616 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 16 Nov 2018 01:11:28 +0100 Subject: [PATCH 056/335] WIP: added DeepRegion --- src/db/db/db.pro | 3 +- src/db/db/dbDeepRegion.cc | 227 ++++++++++++++++++++++++++ src/db/db/dbDeepRegion.h | 141 ++++++++++++++++ src/db/db/dbDeepShapeStore.cc | 61 ++++++- src/db/db/dbDeepShapeStore.h | 26 ++- src/db/db/dbHierarchyBuilder.cc | 11 +- src/db/db/dbRecursiveShapeIterator.cc | 21 ++- src/db/db/dbRegion.cc | 11 ++ src/db/db/dbRegion.h | 21 ++- 9 files changed, 503 insertions(+), 19 deletions(-) create mode 100644 src/db/db/dbDeepRegion.cc create mode 100644 src/db/db/dbDeepRegion.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index c0b0d45c6..7d2d2d563 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -139,7 +139,8 @@ SOURCES = \ dbDeepShapeStore.cc \ dbHierarchyBuilder.cc \ dbLocalOperation.cc \ - dbHierProcessor.cc + dbHierProcessor.cc \ + dbDeepRegion.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc new file mode 100644 index 000000000..78fa48d95 --- /dev/null +++ b/src/db/db/dbDeepRegion.cc @@ -0,0 +1,227 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbDeepRegion.h" +#include "dbDeepShapeStore.h" +#include "dbEmptyRegion.h" +#include "dbRegion.h" +#include "dbShapeProcessor.h" +#include "dbFlatRegion.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// DeepRegion implementation + +DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) + : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)) +{ + init (); +} + +DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics, double area_ratio, size_t max_vertex_count) + : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)) +{ + init (); + + tl_assert (trans.is_unity ()); // TODO: implement + set_merged_semantics (merged_semantics); +} + +DeepRegion::DeepRegion () + : AsIfFlatRegion () +{ + init (); +} + +DeepRegion::~DeepRegion () +{ + // .. nothing yet .. +} + +DeepRegion::DeepRegion (const DeepRegion &other) + : AsIfFlatRegion (other), + m_deep_layer (other.m_deep_layer), + m_merged_polygons (other.m_merged_polygons), + m_merged_polygons_valid (other.m_merged_polygons_valid) +{ + // .. nothing yet .. +} + +void DeepRegion::init () +{ + m_merged_polygons_valid = false; + m_merged_polygons.clear (); +} + +RegionDelegate * +DeepRegion::clone () const +{ + return new DeepRegion (*this); +} + +void DeepRegion::merged_semantics_changed () +{ + // .. nothing yet .. +} + +RegionIteratorDelegate * +DeepRegion::begin () const +{ + return new DeepRegionIterator (begin_iter ().first); +} + +RegionIteratorDelegate * +DeepRegion::begin_merged () const +{ + if (! merged_semantics ()) { + return begin (); + } else { + ensure_merged_polygons_valid (); + return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); + } +} + +std::pair +DeepRegion::begin_iter () const +{ + const db::Layout *layout = m_deep_layer.layout (); + if (layout->cells () == 0) { + + return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); + + } else { + + const db::Cell &top_cell = layout->cell (*layout->begin_top_down ()); + db::RecursiveShapeIterator iter (*m_deep_layer.layout (), top_cell, m_deep_layer.layer ()); + return std::make_pair (iter, db::ICplxTrans ()); + + } +} + +std::pair +DeepRegion::begin_merged_iter () const +{ + if (! merged_semantics ()) { + return begin_iter (); + } else { + ensure_merged_polygons_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); + } +} + +bool +DeepRegion::empty () const +{ + return begin_iter ().first.at_end (); +} + +bool +DeepRegion::is_merged () const +{ + return false; +} + +const db::Polygon * +DeepRegion::nth (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to polygons is available only for flat regions"))); +} + +bool +DeepRegion::has_valid_polygons () const +{ + return false; +} + +bool +DeepRegion::has_valid_merged_polygons () const +{ + return merged_semantics (); +} + +const db::RecursiveShapeIterator * +DeepRegion::iter () const +{ + return 0; +} + +bool +DeepRegion::equals (const Region &other) const +{ + const DeepRegion *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_deep_layer.layout () == m_deep_layer.layout () + && other_delegate->m_deep_layer.layer () == m_deep_layer.layer ()) { + return true; + } else { + return AsIfFlatRegion::equals (other); + } +} + +bool +DeepRegion::less (const Region &other) const +{ + const DeepRegion *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_deep_layer.layout () == m_deep_layer.layout ()) { + return other_delegate->m_deep_layer.layer () < m_deep_layer.layer (); + } else { + return AsIfFlatRegion::less (other); + } +} + +void +DeepRegion::ensure_merged_polygons_valid () const +{ + if (! m_merged_polygons_valid) { + + m_merged_polygons.clear (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (0); + db::ShapeGenerator pc (m_merged_polygons); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + m_merged_polygons_valid = true; + + } +} + +} + diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h new file mode 100644 index 000000000..415973bf8 --- /dev/null +++ b/src/db/db/dbDeepRegion.h @@ -0,0 +1,141 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbDeepRegion +#define HDR_dbDeepRegion + +#include "dbCommon.h" + +#include "dbAsIfFlatRegion.h" +#include "dbDeepShapeStore.h" + +namespace db { + +/** + * @brief An iterator delegate for the deep region + */ +class DB_PUBLIC DeepRegionIterator + : public RegionIteratorDelegate +{ +public: + typedef db::Polygon value_type; + + DeepRegionIterator (const db::RecursiveShapeIterator &iter) + : m_iter (iter) + { + set (); + } + + virtual bool at_end () const + { + return m_iter.at_end (); + } + + virtual void increment () + { + ++m_iter; + set (); + } + + virtual const value_type *get () const + { + return &m_tmp; + } + + virtual RegionIteratorDelegate *clone () const + { + return new DeepRegionIterator (*this); + } + +private: + friend class Region; + + db::RecursiveShapeIterator m_iter; + mutable value_type m_tmp; + + void set () const + { + if (! m_iter.at_end ()) { + m_iter->polygon (m_tmp); + } + } +}; + +/** + * @brief A flat, polygon-set delegate + */ +class DB_PUBLIC DeepRegion + : public AsIfFlatRegion +{ +public: + typedef db::layer polygon_layer_type; + typedef polygon_layer_type::iterator polygon_iterator_type; + + DeepRegion (); + DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio = 3.0, size_t max_vertex_count = 16); + DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 3.0, size_t max_vertex_count = 16); + + DeepRegion (const DeepRegion &other); + + virtual ~DeepRegion (); + + RegionDelegate *clone () const; + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + + virtual bool is_merged () const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + virtual bool has_valid_merged_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + +protected: + virtual void merged_semantics_changed (); + +private: + DeepRegion &operator= (const DeepRegion &other); + + DeepLayer m_deep_layer; + // @@@ have hierarchical merged polygons later + mutable db::Shapes m_merged_polygons; + mutable bool m_merged_polygons_valid; + + void init (); + void ensure_merged_polygons_valid () const; +}; + +} + +#endif + diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index abb1469e8..e9a317ae9 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -22,10 +22,19 @@ #include "dbDeepShapeStore.h" +#include "tlTimer.h" namespace db { +// ---------------------------------------------------------------------------------- + +DeepLayer::DeepLayer () + : mp_store (), m_layout (0), m_layer (0) +{ + // .. nothing yet .. +} + DeepLayer::DeepLayer (const DeepLayer &x) : mp_store (x.mp_store), m_layout (x.m_layout), m_layer (x.m_layer) { @@ -38,21 +47,65 @@ DeepLayer::DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int l // .. nothing yet .. } - +// ---------------------------------------------------------------------------------- DeepShapeStore::DeepShapeStore () { - // @@@ + // .. nothing yet .. } DeepShapeStore::~DeepShapeStore () { - // @@@ + // .. nothing yet .. } DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count) { - return DeepLayer (0, 0, 0); // @@@ + unsigned int layout_index = 0; + unsigned int layer_index = 0; + + layout_map_type::iterator l = m_layout_map.find (si); + if (l == m_layout_map.end ()) { + + layout_index = (unsigned int) m_layouts.size (); + + m_layouts.push_back (new db::Layout ()); + m_layouts.back ().dbu (si.layout ()->dbu ()); + layer_index = m_layouts.back ().insert_layer (); + + m_builders.push_back (new db::HierarchyBuilder (&m_layouts.back (), layer_index)); + + m_layout_map[si] = layout_index; + + } else { + + layout_index = l->second; + layer_index = m_layouts[layout_index].insert_layer (); + + m_builders[layout_index].set_target_layer (layer_index); + + } + + // The chain of operators for producing clipped and reduced polygon references + db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& m_layouts[layout_index]); + db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count); + db::ClippingHierarchyBuilderShapeReceiver clip (&red); + + // Build the working hierarchy from the recursive shape iterator + try { + + tl::SelfTimer timer (tl::to_string (tr ("Building working hierarchy"))); + + m_builders[layout_index].set_shape_receiver (&clip); + db::RecursiveShapeIterator (si).push (& m_builders[layout_index]); + m_builders[layout_index].set_shape_receiver (0); + + } catch (...) { + m_builders[layout_index].set_shape_receiver (0); + throw; + } + + return DeepLayer (this, layout_index, layer_index); } } diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index f76805fd1..c08bfb44d 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -27,8 +27,10 @@ #include "dbCommon.h" #include "tlObject.h" +#include "tlStableVector.h" #include "dbLayout.h" #include "dbRecursiveShapeIterator.h" +#include "dbHierarchyBuilder.h" #include #include @@ -46,6 +48,11 @@ class DeepShapeStore; class DB_PUBLIC DeepLayer { public: + /** + * @brief Default constructor + */ + DeepLayer (); + /** * @brief Destructor */ @@ -63,11 +70,15 @@ public: /** * @brief Gets the layout object - * * The return value is guaranteed to be non-null. */ db::Layout *layout (); + /** + * @brief Gets the layout object (const version) + */ + const db::Layout *layout () const; + /** * @brief Gets the layer */ @@ -89,6 +100,14 @@ private: unsigned int m_layer; }; +struct DB_PUBLIC RecursiveShapeIteratorCompareForTargetHierarchy +{ + bool operator () (const db::RecursiveShapeIterator &a, const db::RecursiveShapeIterator &b) const + { + return db::compare_iterators_with_respect_to_target_hierarchy (a, b) < 0; + } +}; + /** * @brief The "deep shape store" is a working model for the hierarchical ("deep") processor * @@ -128,10 +147,15 @@ public: DeepLayer create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio = 3.0, size_t max_vertex_count = 16); private: + typedef std::map layout_map_type; + // no copying DeepShapeStore (const DeepShapeStore &); DeepShapeStore &operator= (const DeepShapeStore &); + tl::stable_vector m_layouts; + tl::stable_vector m_builders; + layout_map_type m_layout_map; }; } diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index be5945412..383f2b685 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -125,13 +125,13 @@ static std::pair > compute_clip_variant (const db::Box & HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, HierarchyBuilderShapeReceiver *pipe) : mp_target (target), m_initial_pass (true), m_target_layer (target_layer) { - mp_pipe = pipe ? pipe : &def_inserter; + set_shape_receiver (pipe); } HierarchyBuilder::HierarchyBuilder (db::Layout *target, HierarchyBuilderShapeReceiver *pipe) : mp_target (target), m_initial_pass (true), m_target_layer (0) { - mp_pipe = pipe ? pipe : &def_inserter; + set_shape_receiver (pipe); } HierarchyBuilder::~HierarchyBuilder () @@ -142,7 +142,7 @@ HierarchyBuilder::~HierarchyBuilder () void HierarchyBuilder::set_shape_receiver (HierarchyBuilderShapeReceiver *pipe) { - mp_pipe = pipe; + mp_pipe = pipe ? pipe : &def_inserter; } void @@ -191,8 +191,9 @@ HierarchyBuilder::end (const RecursiveShapeIterator * /*iter*/) m_initial_pass = false; m_cells_seen.clear (); - mp_initial_cell = m_cell_stack.back (); - m_cell_stack.pop_back (); + mp_initial_cell = m_cell_stack.front (); + m_cell_stack.clear (); + m_cm_entry = cell_map_type::const_iterator (); } void diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index d55bce34c..18173f2ff 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -961,14 +961,23 @@ RecursiveShapeIterator::push (RecursiveShapeReceiver *receiver) receiver->begin (this); - validate (receiver); + try { + + validate (receiver); + + while (! at_end ()) { + receiver->shape (this, *m_shape, m_trans, m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + next (receiver); + } + + receiver->end (this); + + } catch (...) { + + receiver->end (this); + throw; - while (! at_end ()) { - receiver->shape (this, *m_shape, m_trans, m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); - next (receiver); } - - receiver->end (this); } } diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 74f8039c5..b9ced87ec 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -25,6 +25,7 @@ #include "dbOriginalLayerRegion.h" #include "dbEmptyRegion.h" #include "dbFlatRegion.h" +#include "dbDeepRegion.h" namespace db { @@ -74,6 +75,16 @@ Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, b mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics); } +Region::Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) +{ + mp_delegate = new DeepRegion (si, dss, area_ratio, max_vertex_count); +} + +Region::Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics, double area_ratio, size_t max_vertex_count) +{ + mp_delegate = new DeepRegion (si, dss, trans, merged_semantics, area_ratio, max_vertex_count); +} + const db::RecursiveShapeIterator & Region::iter () const { diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index c0772dc89..4a4cc4e9e 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -38,6 +38,7 @@ namespace db { class EdgeFilterBase; class FlatRegion; class EmptyRegion; +class DeepShapeStore; /** * @brief A base class for polygon filters @@ -532,7 +533,7 @@ public: /** * @brief Constructor from a RecursiveShapeIterator * - * Creates a region from a recursive shape iterator. This allows to feed a region + * Creates a region from a recursive shape iterator. This allows feeding a region * from a hierarchy of cells. */ Region (const RecursiveShapeIterator &si); @@ -540,12 +541,28 @@ public: /** * @brief Constructor from a RecursiveShapeIterator with a transformation * - * Creates a region from a recursive shape iterator. This allows to feed a region + * Creates a region from a recursive shape iterator. This allows feeding a region * from a hierarchy of cells. The transformation is useful to scale to a specific * DBU for example. */ Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); + /** + * @brief Constructor from a RecursiveShapeIterator providing a deep representation + * + * This version will create a hierarchical region. The DeepShapeStore needs to be provided + * during the lifetime of the region and acts as a heap for optimized data. + * + * "area_ratio" and "max_vertex_count" are optimization parameters for the + * shape splitting algorithm. + */ + Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio = 3.0, size_t max_vertex_count = 16); + + /** + * @brief Constructor from a RecursiveShapeIterator providing a deep representation with transformation + */ + Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 3.0, size_t max_vertex_count = 16); + /** * @brief Gets the underlying delegate object */ From a438dfd6f087c6a99bc2d627020f997f87d30590 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 Nov 2018 00:16:13 +0100 Subject: [PATCH 057/335] WIP: deep region debugged, GSI binding, tests. --- src/db/db/dbAsIfFlatRegion.cc | 9 ++ src/db/db/dbAsIfFlatRegion.h | 2 + src/db/db/dbCellMapping.h | 13 +- src/db/db/dbDeepRegion.cc | 66 ++++++++- src/db/db/dbDeepRegion.h | 54 +------- src/db/db/dbDeepShapeStore.cc | 133 ++++++++++++++++++- src/db/db/dbDeepShapeStore.h | 73 +++++++++- src/db/db/dbEmptyRegion.h | 2 + src/db/db/dbFlatRegion.cc | 5 + src/db/db/dbFlatRegion.h | 2 + src/db/db/dbHierarchyBuilder.cc | 11 +- src/db/db/dbHierarchyBuilder.h | 38 +++++- src/db/db/dbLayout.cc | 7 + src/db/db/dbLayout.h | 10 ++ src/db/db/dbRegion.h | 8 ++ src/db/db/dbRegionDelegate.h | 2 + src/db/db/gsiDeclDbCell.cc | 3 - src/db/db/gsiDeclDbLayout.cc | 13 ++ src/db/db/gsiDeclDbRegion.cc | 42 ++++++ src/db/unit_tests/dbDeepRegionTests.cc | 113 ++++++++++++++++ src/db/unit_tests/dbHierarchyBuilderTests.cc | 132 ++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 3 +- testdata/algo/deep_region_au1.gds | Bin 0 -> 9250 bytes testdata/algo/deep_region_l1.gds | Bin 0 -> 2762 bytes testdata/ruby/dbRegionTest.rb | 29 ++++ 25 files changed, 699 insertions(+), 71 deletions(-) create mode 100644 src/db/unit_tests/dbDeepRegionTests.cc create mode 100644 testdata/algo/deep_region_au1.gds create mode 100644 testdata/algo/deep_region_l1.gds diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 50bb03d83..46af6c33b 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1447,6 +1447,15 @@ AsIfFlatRegion::add (const Region &other) const } } +void +AsIfFlatRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + shapes.insert (*p); + } +} + bool AsIfFlatRegion::equals (const Region &other) const { diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index e9cedaaa8..b932903e4 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -201,6 +201,8 @@ public: virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + protected: void update_bbox (const db::Box &box); void invalidate_bbox (); diff --git a/src/db/db/dbCellMapping.h b/src/db/db/dbCellMapping.h index 66cb0a01c..c50001a0c 100644 --- a/src/db/db/dbCellMapping.h +++ b/src/db/db/dbCellMapping.h @@ -179,6 +179,17 @@ public: return m_b2a_mapping; } + /** + * @brief Creates mappings for all cells not mapped yet + * + * When constructing a cell mapping by explicit mapping (map (a, b)), some cells may be + * left unmapped. This method allows creating mappings for these missing cells by adding + * new cells and the corresponding instances into the target layout_a. + * + * The returned vector lists the new cells. + */ + std::vector create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b); + private: void extract_unique (std::map >::const_iterator cand, std::map &unique_mapping, @@ -187,8 +198,6 @@ private: void dump_mapping (const std::map > &candidates, const db::Layout &layout_a, const db::Layout &layout_b); - std::vector create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b); - std::map m_b2a_mapping; }; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 78fa48d95..d5c1cbbfa 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -31,17 +31,73 @@ namespace db { +namespace +{ + /** + * @brief An iterator delegate for the deep region + * TODO: this is kind of redundant with OriginalLayerIterator .. + */ + class DB_PUBLIC DeepRegionIterator + : public RegionIteratorDelegate + { + public: + typedef db::Polygon value_type; + + DeepRegionIterator (const db::RecursiveShapeIterator &iter) + : m_iter (iter) + { + set (); + } + + virtual bool at_end () const + { + return m_iter.at_end (); + } + + virtual void increment () + { + ++m_iter; + set (); + } + + virtual const value_type *get () const + { + return &m_polygon; + } + + virtual RegionIteratorDelegate *clone () const + { + return new DeepRegionIterator (*this); + } + + private: + friend class Region; + + db::RecursiveShapeIterator m_iter; + mutable value_type m_polygon; + + void set () const + { + if (! m_iter.at_end ()) { + m_iter.shape ().polygon (m_polygon); + m_polygon.transform (m_iter.trans (), false); + } + } + }; + +} + // ------------------------------------------------------------------------------------------------------------- // DeepRegion implementation DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) - : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)) + : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)), m_merged_polygons (false) { init (); } DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics, double area_ratio, size_t max_vertex_count) - : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)) + : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)), m_merged_polygons (false) { init (); @@ -223,5 +279,11 @@ DeepRegion::ensure_merged_polygons_valid () const } } +void +DeepRegion::insert_into (db::Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + m_deep_layer.insert_into (layout, into_cell, into_layer); +} + } diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 415973bf8..147162aa0 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -32,57 +32,7 @@ namespace db { /** - * @brief An iterator delegate for the deep region - */ -class DB_PUBLIC DeepRegionIterator - : public RegionIteratorDelegate -{ -public: - typedef db::Polygon value_type; - - DeepRegionIterator (const db::RecursiveShapeIterator &iter) - : m_iter (iter) - { - set (); - } - - virtual bool at_end () const - { - return m_iter.at_end (); - } - - virtual void increment () - { - ++m_iter; - set (); - } - - virtual const value_type *get () const - { - return &m_tmp; - } - - virtual RegionIteratorDelegate *clone () const - { - return new DeepRegionIterator (*this); - } - -private: - friend class Region; - - db::RecursiveShapeIterator m_iter; - mutable value_type m_tmp; - - void set () const - { - if (! m_iter.at_end ()) { - m_iter->polygon (m_tmp); - } - } -}; - -/** - * @brief A flat, polygon-set delegate + * @brief A deep, polygon-set delegate */ class DB_PUBLIC DeepRegion : public AsIfFlatRegion @@ -120,6 +70,8 @@ public: virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + protected: virtual void merged_semantics_changed (); diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index e9a317ae9..b17a15b39 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -22,6 +22,9 @@ #include "dbDeepShapeStore.h" +#include "dbCellMapping.h" +#include "dbLayoutUtils.h" + #include "tlTimer.h" namespace db @@ -47,16 +50,57 @@ DeepLayer::DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int l // .. nothing yet .. } -// ---------------------------------------------------------------------------------- - -DeepShapeStore::DeepShapeStore () +DeepLayer::~DeepLayer () { // .. nothing yet .. } +void +DeepLayer::insert_into (db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + check_dss (); + const_cast (mp_store.get ())->insert (*this, into_layout, into_cell, into_layer); +} + +db::Layout * +DeepLayer::layout () +{ + check_dss (); + return mp_store->layout (m_layout); +} + +const db::Layout * +DeepLayer::layout () const +{ + check_dss (); + return const_cast (mp_store.get ())->layout (m_layout); +} + +void +DeepLayer::check_dss () const +{ + if (mp_store.get () == 0) { + throw tl::Exception (tl::to_string (tr ("Heap lost: the DeepShapeStore container no longer exists"))); + } +} + +// ---------------------------------------------------------------------------------- + +static size_t s_instance_count = 0; + +DeepShapeStore::DeepShapeStore () +{ + ++s_instance_count; +} + DeepShapeStore::~DeepShapeStore () { - // .. nothing yet .. + --s_instance_count; +} + +size_t DeepShapeStore::instance_count () +{ + return s_instance_count; } DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count) @@ -108,5 +152,86 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator return DeepLayer (this, layout_index, layer_index); } +void +DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) +{ + const db::Layout *source_layout = deep_layer.layout (); + if (source_layout->begin_top_down () == source_layout->end_top_cells ()) { + // empty source - nothing to do. + return; + } + + db::cell_index_type source_top = *source_layout->begin_top_down(); + + db::HierarchyBuilder &original_builder = m_builders [deep_layer.layout_index ()]; + + // derive a cell mapping for source to target. We employ a + + DeliveryMappingCacheKey key (deep_layer.layout_index (), into_layout, into_cell); + + std::map::iterator cm = m_delivery_mapping_cache.find (key); + if (cm == m_delivery_mapping_cache.end ()) { + + cm = m_delivery_mapping_cache.insert (std::make_pair (key, db::CellMapping ())).first; + + if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell ()) { + + // This is the case of mapping back to the original. In this case we can use the information + // provided inside the original hierarchy builders. They list the source cells and the target cells + // create from them. We need to consider however, that the hierarchy builder is allowed to create + // variants which we cannot map. + + bool any_skipped = false; + + for (HierarchyBuilder::cell_map_type::const_iterator m = original_builder.begin_cell_map (); m != original_builder.end_cell_map (); ++m) { + + HierarchyBuilder::cell_map_type::const_iterator mm = m; + ++mm; + bool skip = false; + while (mm != original_builder.end_cell_map () && mm->first.first == m->first.first) { + // we have cell variants and cannot simply map + ++mm; + ++m; + skip = true; + } + + if (! skip) { + cm->second.map (m->first.first, m->second); + } else { + any_skipped = true; + } + + } + + if (any_skipped) { + // Add new cells for the variants + cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top); + } + + } else if (into_layout->cells () == 1) { + + // Another simple case is mapping into an empty (or single-top-cell-only) layout, where we can use "create_from_single_full". + cm->second.create_single_mapping_full (*into_layout, into_cell, *source_layout, source_top); + + } else { + + cm->second.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_top); + + } + + } + + // Actually copy the shapes + + db::ICplxTrans trans (source_layout->dbu () / into_layout->dbu ()); + + std::map lm; + lm.insert (std::make_pair (deep_layer.layer (), into_layer)); + + std::vector source_cells; + source_cells.push_back (source_top); + db::copy_shapes (*into_layout, *source_layout, trans, source_cells, cm->second.table (), lm); +} + } diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index c08bfb44d..227213d36 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -31,6 +31,7 @@ #include "dbLayout.h" #include "dbRecursiveShapeIterator.h" #include "dbHierarchyBuilder.h" +#include "gsiObject.h" #include #include @@ -87,6 +88,19 @@ public: return m_layer; } + /** + * @brief Gets the layout index + */ + unsigned int layout_index () const + { + return m_layout; + } + + /** + * @brief Inserts the layer into the given layout, starting from the given cell and into the given layer + */ + void insert_into (Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) const; + private: friend class DeepShapeStore; @@ -95,6 +109,8 @@ private: */ DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer); + void check_dss () const; + tl::weak_ptr mp_store; unsigned int m_layout; unsigned int m_layer; @@ -121,7 +137,7 @@ struct DB_PUBLIC RecursiveShapeIteratorCompareForTargetHierarchy * algorithms for doing the preparation and transfer. */ class DB_PUBLIC DeepShapeStore - : public tl::Object + : public tl::Object, public gsi::ObjectBase { public: /** @@ -146,7 +162,24 @@ public: */ DeepLayer create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio = 3.0, size_t max_vertex_count = 16); + /** + * @brief Inserts the deep layer's into some target layout + */ + void insert (const DeepLayer &layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer); + + /** + * @brief For testing + */ + static size_t instance_count (); + private: + friend class DeepLayer; + + db::Layout *layout (unsigned int n) + { + return &m_layouts [n]; + } + typedef std::map layout_map_type; // no copying @@ -156,9 +189,47 @@ private: tl::stable_vector m_layouts; tl::stable_vector m_builders; layout_map_type m_layout_map; + + struct DeliveryMappingCacheKey + { + // NOTE: we shouldn't keep pointers here as the layouts may get deleted and recreated with the same address. + // But as we don't access these objects that's fairly safe. + DeliveryMappingCacheKey (unsigned int _from_index, db::Layout *_into_layout, db::cell_index_type _into_cell) + : from_index (_from_index), into_layout (_into_layout), into_cell (_into_cell) + { + // .. nothing yet .. + } + + bool operator< (const DeliveryMappingCacheKey &other) const + { + if (from_index != other.from_index) { + return from_index < other.from_index; + } + if (into_layout != other.into_layout) { + return into_layout < other.into_layout; + } + return into_cell m_delivery_mapping_cache; }; } +namespace tl +{ + + // disable copying of the deep shape store object + template <> struct type_traits : public type_traits { + typedef tl::false_tag has_copy_constructor; + }; + +} + #endif diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index 696853af1..da0327ba0 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -120,6 +120,8 @@ public: virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; + virtual void insert_into (Layout *, db::cell_index_type, unsigned int) const { } + private: EmptyRegion &operator= (const EmptyRegion &other); }; diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index 411dff8e5..ae1868c23 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -362,6 +362,11 @@ const db::RecursiveShapeIterator *FlatRegion::iter () const return 0; } +void FlatRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + layout->cell (into_cell).shapes (into_layer).insert (m_polygons); +} + void FlatRegion::insert (const db::Box &box) { diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index 72e712a6d..cbae0ba71 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -109,6 +109,8 @@ public: virtual size_t size () const; virtual bool is_merged () const; + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + virtual RegionDelegate *merged_in_place (); virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); virtual RegionDelegate *merged () const; diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 383f2b685..c07523fb4 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -62,16 +62,19 @@ compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIter if (iter1.has_complex_region () && iter1.complex_region () != iter2.complex_region ()) { return iter1.complex_region () < iter2.complex_region () ? -1 : 1; } + if (iter1.region () != iter2.region ()) { + return iter1.region () < iter2.region () ? -1 : 1; + } if (iter1.multiple_layers () != iter2.multiple_layers ()) { return iter1.multiple_layers () < iter2.multiple_layers () ? -1 : 1; } if (iter1.multiple_layers ()) { if (iter1.layers () != iter2.layers ()) { - return iter1.layers () < iter2.layers (); + return iter1.layers () < iter2.layers () ? -1 : 1; } } else { if (iter1.layer () != iter2.layer ()) { - return iter1.layer () < iter2.layer (); + return iter1.layer () < iter2.layer () ? -1 : 1; } } } @@ -161,9 +164,9 @@ void HierarchyBuilder::begin (const RecursiveShapeIterator *iter) { if (m_initial_pass) { - m_ref_iter = *iter; + m_source = *iter; } else { - tl_assert (compare_iterators_with_respect_to_target_hierarchy (m_ref_iter, *iter) == 0); + tl_assert (compare_iterators_with_respect_to_target_hierarchy (m_source, *iter) == 0); } m_cell_stack.clear (); diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index c982ec384..37176b340 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -41,7 +41,7 @@ namespace db * This function will return -1, 0 or 1 depending on whether the two iterators * can be used with the same builder (0) or whether they are less (-1) or greater (1). */ -int compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2); +int DB_PUBLIC compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2); /** * @brief A class to receive shapes from the hierarchy builder @@ -199,18 +199,50 @@ public: void reset (); /** - * @brief Gets the initial cell the builder produced + * @brief Gets the initial cell the builder produced in the target layout */ db::Cell *initial_cell () { return mp_initial_cell; } + /** + * @brief Gets the target layout + */ + db::Layout *target () + { + return mp_target.get (); + } + + /** + * @brief Gets the recursive shape iterator the data was taken from + */ + const db::RecursiveShapeIterator &source () const + { + return m_source; + } + + /** + * @brief Gets the iterator for the cell map + */ + cell_map_type::const_iterator begin_cell_map () const + { + return m_cell_map.begin (); + } + + /** + * @brief Gets the iterator for the cell map (end) + */ + cell_map_type::const_iterator end_cell_map () const + { + return m_cell_map.end (); + } + private: tl::weak_ptr mp_target; HierarchyBuilderShapeReceiver *mp_pipe; bool m_initial_pass; - db::RecursiveShapeIterator m_ref_iter; + db::RecursiveShapeIterator m_source; cell_map_type m_cell_map; std::set m_cells_seen; cell_map_type::const_iterator m_cm_entry; diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 0380deb12..91fd9f995 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -31,6 +31,7 @@ #include "dbLibraryProxy.h" #include "dbLibraryManager.h" #include "dbLibrary.h" +#include "dbRegion.h" #include "tlTimer.h" #include "tlLog.h" #include "tlInternational.h" @@ -638,6 +639,12 @@ Layout::delete_cell (cell_index_type id) } } +void +Layout::insert (db::cell_index_type cell, int layer, const db::Region ®ion) +{ + region.insert_into (this, cell, layer); +} + void Layout::flatten (const db::Cell &source_cell, db::Cell &target_cell, const db::ICplxTrans &t, int levels) { diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 8ebaf1fbc..eb48b7727 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -61,6 +61,7 @@ class Library; class LibraryProxy; class CellMapping; class LayerMapping; +class Region; template class generic_repository; typedef generic_repository GenericRepository; @@ -1090,6 +1091,15 @@ public: */ void flatten (db::Cell &cell, int levels, bool prune = false); + /** + * @brief Inserts a region (potentially hierarchical) into the given cell and layer + * + * If the region is flat (conceptionally), it will be put into the cell. + * If the region is hierarchical, a cell hierarchy will be built below the + * given cell. + */ + void insert (db::cell_index_type cell, int layer, const db::Region ®ion); + /** * @brief Delete a cell plus all subcells * diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 4a4cc4e9e..c2969f45e 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1685,6 +1685,14 @@ public: return mp_delegate->less (other); } + /** + * @brief Less operator + */ + void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const + { + return mp_delegate->insert_into (layout, into_cell, into_layer); + } + private: friend class Edges; friend class EdgePairs; diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index ecf10d292..626317f50 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -179,6 +179,8 @@ public: virtual bool equals (const Region &other) const = 0; virtual bool less (const Region &other) const = 0; + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const = 0; + protected: const std::string &progress_desc () const { diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 9784a56af..220fd40f8 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1356,7 +1356,6 @@ static std::vector copy_tree (db::Cell *cell, const db::Cel throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout"))); } - db::PropertyMapper pm (*target_layout, *source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); db::CellMapping cm; @@ -1387,7 +1386,6 @@ static void copy_tree_shapes2 (db::Cell *cell, const db::Cell &source_cell, cons throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout"))); } - db::PropertyMapper pm (*target_layout, *source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); db::LayerMapping lm; @@ -1413,7 +1411,6 @@ static void copy_tree_shapes3 (db::Cell *cell, const db::Cell &source_cell, cons throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout"))); } - db::PropertyMapper pm (*target_layout, *source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); std::vector source_cells; diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 48c60db98..537cd5ada 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -32,6 +32,7 @@ #include "dbLibraryManager.h" #include "dbPCellDeclaration.h" #include "dbHash.h" +#include "dbRegion.h" #include "tlStream.h" namespace gsi @@ -1179,6 +1180,18 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.20.\n" ) + + gsi::method ("insert", (void (db::Layout::*) (db::cell_index_type, unsigned int, const db::Region &)) &db::Layout::insert, + gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), + "@brief Inserts a region into the given cell and layer\n" + "If the region is (conceptionally) a flat region, it will be inserted into the cell's shapes " + "list as a flat sequence of polygons.\n" + "If the region is a deep (hierarchical) region, it will create a subhierarchy below the given " + "cell and it's shapes will be put into the respective cells. Suitable subcells will be picked " + "for inserting the shapes. If a hierarchy already exists below the given cell, the algorithm will " + "try to reuse this hierarchy.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + gsi::method_ext ("flatten", &flatten, "@brief Flattens the given cell\n" "@args cell_index, levels, prune\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 7cb106fa8..12df8eeee 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -26,6 +26,8 @@ #include "dbPolygonTools.h" #include "dbLayoutUtils.h" #include "dbShapes.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" #include "tlGlobPattern.h" #include @@ -213,6 +215,11 @@ static db::Region *new_si (const db::RecursiveShapeIterator &si) return new db::Region (si); } +static db::Region *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) +{ + return new db::Region (si, dss, area_ratio, max_vertex_count); +} + static db::Region *new_si2 (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) { return new db::Region (si, trans); @@ -712,6 +719,25 @@ static Container *decompose_trapezoids (const db::Region *r, int mode) int td_simple (); int po_any (); +Class decl_DeepShapeStore ("db", "DeepShapeStore", + method ("instance_count", db::DeepShapeStore::instance_count, "@hide"), + "@brief An opaque layout heap for the deep region processor\n" + "\n" + "This class is used for keeping intermediate, hierarchical data for the " + "deep region processor. It is used in conjunction with the region " + "constructor to create a deep (hierarchical) region." + "\n" + "@code\n" + "layout = ... # a layout\n" + "layer = ... # a layer\n" + "cell = ... # a cell (initial cell for the deep region)\n" + "dss = RBA::DeepShapeStore::new\n" + "region = RBA::Region::new(cell.begin(layer), dss)\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.26.\n" +); + Class decl_Region ("db", "Region", constructor ("new", &new_v, "@brief Default constructor\n" @@ -789,6 +815,22 @@ Class decl_Region ("db", "Region", "r = RBA::Region::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + + constructor ("new", &new_sid, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("area_ratio", 3.0), gsi::arg ("max_vertex_count", size_t (16)), + "@brief Constructor for a deep region from a hierarchical shape set\n" + "\n" + "This constructor creates a hierarchical region. Use a \\DeepShapeStore object to " + "supply the hierarchical heap. See \\DeepShapeStore for more details.\n" + "\n" + "'area_ratio' and 'max_vertex' supply two optimization parameters which control how " + "big polygons are split to reduce the region's polygon complexity.\n" + "\n" + "@param shape_iterator The recursive shape iterator which delivers the hierarchy to take\n" + "@param deep_shape_store The hierarchical heap (see there)\n" + "@param area_ratio The maximum ratio of bounding box to polygon area before polygons are split\n" + "@param" + "\n" + "This method has been introduced in version 0.26.\n" + ) + constructor ("new", &new_texts, gsi::arg("shape_iterator"), gsi::arg ("expr"), gsi::arg ("as_pattern", true), "@brief Constructor from a text set\n" "\n" diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc new file mode 100644 index 000000000..a5e582579 --- /dev/null +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -0,0 +1,113 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbHierarchyBuilder.h" +#include "dbReader.h" +#include "dbTestSupport.h" +#include "dbRegion.h" +#include "dbDeepShapeStore.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + + db::DeepShapeStore dss; + db::Layout target; + + // deliberately using vector to force reallocation ... + std::vector regions; + std::vector target_layers; + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + target_layers.push_back (target.insert_layer (*(*li).second)); + + regions.push_back (db::Region (iter, dss)); + + } + + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { + target.insert (target_top_cell_index, target_layers [r - regions.begin ()], *r); + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au1.gds"); +} + +TEST(2) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + + db::DeepShapeStore dss; + db::Layout target; + + // deliberately using vector to force reallocation ... + std::vector > regions; + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int tl = target.insert_layer (*(*li).second); + + db::RecursiveShapeIterator iter1 (ly, ly.cell (top_cell_index), li1, db::Box (2000, -1000, 6000, 4000)); + regions.push_back (std::make_pair (db::Region (iter1, dss), tl)); + + db::RecursiveShapeIterator iter2 (ly, ly.cell (top_cell_index), li1, db::Box (14000, 0, 20000, 3000)); + regions.push_back (std::make_pair (db::Region (iter2, dss), tl)); + + } + + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + for (std::vector >::const_iterator r = regions.begin (); r != regions.end (); ++r) { + target.insert (target_top_cell_index, r->second, r->first); + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au1.gds"); +} + diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc index 1dffa2d9d..84a7394f5 100644 --- a/src/db/unit_tests/dbHierarchyBuilderTests.cc +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -335,3 +335,135 @@ TEST(4_ComplexRegionAndLayoutWithClip) db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au4a.gds"); } +TEST(5_CompareRecursiveShapeIterators) +{ + db::Layout ly; + db::cell_index_type ci = ly.add_cell ("TOP"); + db::cell_index_type ci1 = ly.add_cell ("TOPA"); + + db::Layout ly2; + db::cell_index_type ci2 = ly2.add_cell ("TOP"); + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0); + db::RecursiveShapeIterator iter2 (ly2, ly2.cell (ci2), 0); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci1), 0); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 1); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + } + + { + std::vector ll1; + ll1.push_back (100); + ll1.push_back (101); + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), ll1); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 1); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0); + iter1.max_depth (1); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0); + iter2.max_depth (1); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + + iter2.max_depth (2); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 1, db::Box (0, 1000, 2000, 3000)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + std::vector ll1; + ll1.push_back (100); + ll1.push_back (101); + + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), ll1, db::Box (0, 1000, 2000, 3000)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3001)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::Region r1; + r1.insert (db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, r1); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + } + + { + db::Region r1; + r1.insert (db::Box (0, 1000, 2000, 3000)); + r1.insert (db::Box (0, 4000, 2000, 6000)); + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, r1); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::Region r1; + r1.insert (db::Box (0, 1000, 2000, 3000)); + r1.insert (db::Box (0, 4000, 2000, 6000)); + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, r1); + db::Region r2; + r2.insert (db::Box (0, 1000, 2000, 3000)); + r2.insert (db::Box (0, 4000, 2000, 6000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, r2); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + } + + { + db::Region r1; + r1.insert (db::Box (0, 1000, 2000, 3000)); + r1.insert (db::Box (0, 4000, 2000, 6000)); + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, r1); + db::Region r2; + r2.insert (db::Box (0, 1000, 2000, 3000)); + r2.insert (db::Box (0, 4000, 2000, 6001)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, r2); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } +} + diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index e6b19cd84..5c0d7a9a4 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -55,7 +55,8 @@ SOURCES = \ dbSaveLayoutOptionsTests.cc \ dbHierarchyBuilderTests.cc \ dbRecursiveShapeIteratorTests.cc \ - dbHierProcessorTests.cc + dbHierProcessorTests.cc \ + dbDeepRegionTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/testdata/algo/deep_region_au1.gds b/testdata/algo/deep_region_au1.gds new file mode 100644 index 0000000000000000000000000000000000000000..bfa6b5bde37c2c7396658552e9a72212db726a82 GIT binary patch literal 9250 zcmdT|&x@5+7(VyT%)NJhD3cBb;X-8EiRq}5A&HhzgEGl9NQlrs(87gCgvLNEs+EgY z)UWzq z&*Sx8ulomn3$d<^*XiS&XE_i1&Qm`}$=%s1eazkM?K8ZN-(BvX;o<(czq6wK9OwRq zjd|+fKSRlr{afMr*KXVS*KR}a5}s#;k`p8Q1AXlMyB`Nq5BGmUD0xA@5Hs{&Ss^f47a!?^buHR&@$$qV`gpDEA1BJ$ku3FP^Kb@=RV z<};75^g4Dh5KiSb2HDKQ1W>Hz~@QKz%kdOi z9o#xj7I=MQ)g7yEsyb(O7OHbDPwWr8juBL4{|Ah#b1uhIc>cs|oQG&BFnTVR{($OS z^xFP%ufftQ*pb|K1^)$3?~jL82VU!s+dAS>p#jmhg1nlOvi-p+yfeRV z!YN!=^=0TYS7tZQlxKJ281_fJG>%D5jG{Y;6O4hs#n;30IQ4PTP4<2fC%|rh_q0Fj z50X0%?8i9+PVjNc^D|z7syfwi8goM0`whzpjBkJc9$`OXA?q`I%nk7QL{`&Li#qmZ zk<@Xl7jK zWMRi<_C3-L-xT?F-@OIPfxRD*-RRtel6P-KCrb38bCUWv(HqpyQF3>(UVr?C=}&xP z`a;RaZ`A8g9x?r?L#8j3eDa9t54b1vGX6&UA1C(D&gGRW>YyH;LpfXS^@we}xBY!B zN6A~uk=32>)*pQeOVNQ58+S}!C^;sCm7}0PzSs08Z<@YP^6|Zqek0w#-6_|XyxocP z$3p#wU*r5&aeiPM-)bIxl%wRWtENBvt@BINgmXK#WnZTH4q>!U@tailJnzre$IO|+ z%mewniTgG44!YJ0jTOh)@Pu_a{?G-;5L3kQ3+MyB$Wd}ksMcSF4F~z& zhDl$+F7$dr$w%<*h2QzI^F}$b_5iD6e`1HhrA#_W8FX*)5d3&i*u~8Dq?C;_x{QzkPtS+%e?&E$1WqYjWE3CT|)JpVP7a;GBl7 zyK#RX8jhX;f*XaB1H=2@a81bK=b2>^-aDDBj~TUr4>aHj@iZf z$9L2^Qv8BUC5D0#j9(`Po^?@?ng<}>|fC^<1Ye~{OvCkC7P!SscaSLe0qi@~=3YWhOS zmDgGP1B|;5|8F2IGZo%3gK4r8!(POWB9Ik=LLkLxVi7oD-}Q z=K8Z#QJzX~y|%&%g9@;c_te&7OHPb3AgW;0V+A6rClyLw*PE_AV1@YqRv@06Jv320 zwNUbe0ip`x?;jP2`syeQS$QZYM)#lS!*6(BramhVdvl@Wbrs0ljrV0DD-ZRBtUxv% z?Z4p)G@yduyU)_X;+t>xD@RgKkgF&Hryc)1qgpQ!%|aV#Z@e?t>gSRF-{O~ rh;cAv>zE;*pFeNUM9G0+#}nz#e{b`M)L-0{BlWX!ua9^c9d-WzeKT?l literal 0 HcmV?d00001 diff --git a/testdata/algo/deep_region_l1.gds b/testdata/algo/deep_region_l1.gds new file mode 100644 index 0000000000000000000000000000000000000000..fa837f4e2a317771de909276f480b397e7cf8199 GIT binary patch literal 2762 zcma);(MuFj6vofa&fb|_S2L5skSHmrP>VDpBvIQ+F*MvwY7z9Ymmo+YqCzOak|3xj z>p_C-!M7j~^eH03ryheM_8$lqd~(U1zB6}@Gb7HJpugpu^WAgrJ?Ea46dA@1YEBqd zi89no2k0cV1wWH*sH=nhM2*Q*Pv5h1Ltj6X&*zp;Exaifs5up_%P3VC?HkS$nKqHC zHOL@UXAn_qebR9@?>SEO7?UXRlgM~36g!j5y}sTI>Z=C)Yxo77Mew&zu~*mP^w>#} z^~l@BdY@vaVw4`SEzqoKI*VYH^J|JdIzQ_;zhzDNMt0P=RdruO7E7VxeB**z%ViF|}>#P>V z_J^(1;PBWPGBZS(2I-Y7_eS-OyO3M^oxPc|&JpsegNbP(tbmiej96h?jI86GP;Mx2 z!o)4^SNBvz-_VScSlOe6#>Qz$n4XHdZ`@8R&Na>?N>8C~uZ3b~#^_7P)v0gUe5>mv z6nnBWq?dZ>_qL86QOLWF(($|bBNRI`N{<`e=lVg`A%Cq~Z_w z7)KKp9EWLf@1sYAVpoOG{_Wsb_9y%s4&j)voo~k6JGjd%8WO zPlWSxpJ;vE53Lu99eos>cPU?wy|&=~9%!0lAm)Wo>|lf*_x29=H?L`7_EsKaKOV)d z=0keR&yU(Z!TqdgI-gL-NBHbf?A4VxJv2kC$KEfo-ly1UC|;jl()!GMtrvDKz}S*;g}y{231vs?3Szd;F}DEi%@KB@^*D-?y9AyHe?z;03_*9#~Re|zmR2k|{Q zIyih~1d*6#c7vXZ?i9l=`A6np$d|bm?Q2|!_8lrm`%d&m`#QoCHMExtrD)y2>1f~R zbhK~$yYIu8<^LaKI=l01`#h+8(er@$Kl5N?0CBx|51@ryMa%=C*j2B-doO=ro)AqO zXT2utCYQCIDfVsp(0kf=g}6E72dx>r#}_`uuI57d1A3yH9jw>1X0ZP@)@zDAPCs#1 z>zS;ZyzP_qp5N^~?>*rBey2~A5pNTBdj6uI} literal 0 HcmV?d00001 diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 01d4ea556..3bce9e803 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -769,6 +769,35 @@ class DBRegion_TestClass < TestBase end + # deep region tests + def test_deep1 + + # construction/destruction magic ... + GC.start + assert_equal(RBA::DeepShapeStore::instance_count, 0) + dss = RBA::DeepShapeStore::new + dss._create + assert_equal(RBA::DeepShapeStore::instance_count, 1) + dss = nil + GC.start + assert_equal(RBA::DeepShapeStore::instance_count, 0) + + dss = RBA::DeepShapeStore::new + ly = RBA::Layout::new + ly.read(File.join($ut_testsrc, "testdata", "algo", "deep_region_l1.gds")) + l1 = ly.layer(1, 0) + r = RBA::Region::new(ly.top_cell.begin_shapes_rec(l1), dss) + rf = RBA::Region::new(ly.top_cell.begin_shapes_rec(l1)) + + assert_equal(r.area, 53120000) + assert_equal(rf.area, 53120000) + + # force destroy, so the unit tests pass on the next iteration + dss._destroy + assert_equal(RBA::DeepShapeStore::instance_count, 0) + + end + end load("test_epilogue.rb") From 694730746bf63106ace7e6de3213f41ab8080b86 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 Nov 2018 00:28:32 +0100 Subject: [PATCH 058/335] WIP: added more ruby, python tests. --- testdata/python/dbRegionTest.py | 42 +++++++++++++++++++++++++++++++++ testdata/ruby/dbRegionTest.rb | 16 +++++++++++++ 2 files changed, 58 insertions(+) diff --git a/testdata/python/dbRegionTest.py b/testdata/python/dbRegionTest.py index 0cdb1efc9..1de49db34 100644 --- a/testdata/python/dbRegionTest.py +++ b/testdata/python/dbRegionTest.py @@ -19,6 +19,7 @@ import pya import unittest import sys +import os class DBRegionTest(unittest.TestCase): @@ -39,6 +40,47 @@ class DBRegionTest(unittest.TestCase): r.merge() self.assertEqual(str(r), "(0,100;0,300;50,300;50,350;250,350;250,150;200,150;200,100)") + def test_deep1(self): + + ut_testsrc = os.getenv("TESTSRC") + + # construction/destruction magic ... + self.assertEqual(pya.DeepShapeStore.instance_count(), 0) + dss = pya.DeepShapeStore() + dss._create() + self.assertEqual(pya.DeepShapeStore.instance_count(), 1) + dss = None + self.assertEqual(pya.DeepShapeStore.instance_count(), 0) + + dss = pya.DeepShapeStore() + ly = pya.Layout() + ly.read(os.path.join(ut_testsrc, "testdata", "algo", "deep_region_l1.gds")) + l1 = ly.layer(1, 0) + r = pya.Region(ly.top_cell().begin_shapes_rec(l1), dss) + rf = pya.Region(ly.top_cell().begin_shapes_rec(l1)) + + self.assertEqual(r.area(), 53120000) + self.assertEqual(rf.area(), 53120000) + + ly_new = pya.Layout() + tc = ly_new.add_cell("TOP") + l1 = ly_new.layer(1, 0) + l2 = ly_new.layer(2, 0) + ly_new.insert(tc, l1, r) + ly_new.insert(tc, l2, rf) + + s1 = { } + s2 = { } + for cell in ly_new.each_cell(): + s1[cell.name] = cell.shapes(l1).size() + s2[cell.name] = cell.shapes(l2).size() + self.assertEqual(s1, {"INV2": 1, "TOP": 0, "TRANS": 0}) + self.assertEqual(s2, {"INV2": 0, "TOP": 10, "TRANS": 0}) + + # force destroy, so the unit tests pass on the next iteration + dss = None + self.assertEqual(pya.DeepShapeStore.instance_count(), 0) + # run unit tests if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(DBRegionTest) diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 3bce9e803..493aa7c68 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -792,6 +792,22 @@ class DBRegion_TestClass < TestBase assert_equal(r.area, 53120000) assert_equal(rf.area, 53120000) + ly_new = RBA::Layout::new + tc = ly_new.add_cell("TOP") + l1 = ly_new.layer(1, 0) + l2 = ly_new.layer(2, 0) + ly_new.insert(tc, l1, r) + ly_new.insert(tc, l2, rf) + + s1 = { } + s2 = { } + ly_new.each_cell do |cell| + s1[cell.name] = cell.shapes(l1).size + s2[cell.name] = cell.shapes(l2).size + end + assert_equal(s1, {"INV2"=>1, "TOP"=>0, "TRANS"=>0}) + assert_equal(s2, {"INV2"=>0, "TOP"=>10, "TRANS"=>0}) + # force destroy, so the unit tests pass on the next iteration dss._destroy assert_equal(RBA::DeepShapeStore::instance_count, 0) From cfa0e8431c78e715f9927fcfc505604feafedb51 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 Nov 2018 01:33:58 +0100 Subject: [PATCH 059/335] WIP: implemented AND/NOT in deep region, added tests. --- src/db/db/dbDeepRegion.cc | 69 +++++++++++++++++++++++++ src/db/db/dbDeepRegion.h | 10 ++++ src/db/db/dbDeepShapeStore.cc | 28 ++++++++++ src/db/db/dbDeepShapeStore.h | 45 ++++++++++++++++ src/db/db/gsiDeclDbRegion.cc | 9 ++++ src/db/unit_tests/dbDeepRegionTests.cc | 62 +++++++++++++++++++++- testdata/algo/deep_region_au1.gds | Bin 9250 -> 2570 bytes testdata/algo/deep_region_au2.gds | Bin 0 -> 9250 bytes testdata/algo/deep_region_au3.gds | Bin 0 -> 4074 bytes 9 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 testdata/algo/deep_region_au2.gds create mode 100644 testdata/algo/deep_region_au3.gds diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index d5c1cbbfa..b14e1818c 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -27,6 +27,7 @@ #include "dbRegion.h" #include "dbShapeProcessor.h" #include "dbFlatRegion.h" +#include "dbHierProcessor.h" namespace db { @@ -111,6 +112,12 @@ DeepRegion::DeepRegion () init (); } +DeepRegion::DeepRegion (const DeepLayer &dl) + : AsIfFlatRegion (), m_deep_layer (dl) +{ + init (); +} + DeepRegion::~DeepRegion () { // .. nothing yet .. @@ -285,5 +292,67 @@ DeepRegion::insert_into (db::Layout *layout, db::cell_index_type into_cell, unsi m_deep_layer.insert_into (layout, into_cell, into_layer); } +RegionDelegate * +DeepRegion::and_with (const Region &other) const +{ + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + + if (empty () || other.empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (! other_deep) { + + return AsIfFlatRegion::and_with (other); + + } else { + + return new DeepRegion (and_or_not_with (other_deep, true)); + + } +} + +RegionDelegate * +DeepRegion::not_with (const Region &other) const +{ + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (other.empty () && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatRegion::not_with (other); + + } else { + + return new DeepRegion (and_or_not_with (other_deep, false)); + + } +} + +DeepLayer +DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const +{ + DeepLayer dl_out (m_deep_layer.derived ()); + + db::BoolAndOrNotLocalOperation op (and_op); + + db::LocalProcessor proc (const_cast (m_deep_layer.layout ()), const_cast (m_deep_layer.initial_cell ()), other->deep_layer ().layout (), other->deep_layer ().initial_cell ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + + proc.run (&op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); + + return dl_out; +} + } diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 147162aa0..a69e6ff7f 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -70,13 +70,22 @@ public: virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; + virtual RegionDelegate *and_with (const Region &other) const; + virtual RegionDelegate *not_with (const Region &other) const; + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + const DeepLayer &deep_layer () const + { + return m_deep_layer; + } + protected: virtual void merged_semantics_changed (); private: DeepRegion &operator= (const DeepRegion &other); + DeepRegion (const DeepLayer &dl); DeepLayer m_deep_layer; // @@@ have hierarchical merged polygons later @@ -85,6 +94,7 @@ private: void init (); void ensure_merged_polygons_valid () const; + DeepLayer and_or_not_with(const DeepRegion *other, bool and_op) const; }; } diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index b17a15b39..ed76dbaf5 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -55,6 +55,12 @@ DeepLayer::~DeepLayer () // .. nothing yet .. } +DeepLayer +DeepLayer::derived () const +{ + return DeepLayer (const_cast (mp_store.get ()), m_layout, const_cast (layout ())->insert_layer ()); +} + void DeepLayer::insert_into (db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) const { @@ -76,6 +82,22 @@ DeepLayer::layout () const return const_cast (mp_store.get ())->layout (m_layout); } +db::Cell * +DeepLayer::initial_cell () +{ + db::Layout *ly = layout (); + tl_assert (ly->begin_top_down () != ly->end_top_down ()); + return &ly->cell (*ly->begin_top_down ()); +} + +const db::Cell * +DeepLayer::initial_cell () const +{ + const db::Layout *ly = layout (); + tl_assert (ly->begin_top_down () != ly->end_top_down ()); + return &ly->cell (*ly->begin_top_down ()); +} + void DeepLayer::check_dss () const { @@ -89,6 +111,7 @@ DeepLayer::check_dss () const static size_t s_instance_count = 0; DeepShapeStore::DeepShapeStore () + : m_threads (1) { ++s_instance_count; } @@ -103,6 +126,11 @@ size_t DeepShapeStore::instance_count () return s_instance_count; } +void DeepShapeStore::set_threads (int n) +{ + m_threads = n; +} + DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count) { unsigned int layout_index = 0; diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 227213d36..e920ad788 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -80,6 +80,17 @@ public: */ const db::Layout *layout () const; + /** + * @brief Gets the layout object + * The return value is guaranteed to be non-null. + */ + db::Cell *initial_cell (); + + /** + * @brief Gets the initial cell object (const version) + */ + const db::Cell *initial_cell () const; + /** * @brief Gets the layer */ @@ -101,6 +112,24 @@ public: */ void insert_into (Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) const; + /** + * @brief Creates a derived new deep layer + * Derived layers use the same layout and context, but are initially + * empty layers for use as output layers on the same hierarchy. + */ + DeepLayer derived () const; + + /** + * @brief Gets the shape store object + * This is a pure const version to prevent manipulation of the store. + * This method is intended to fetch configuration options from the store. + */ + const DeepShapeStore *store () const + { + check_dss (); + return mp_store.get (); + } + private: friend class DeepShapeStore; @@ -172,6 +201,21 @@ public: */ static size_t instance_count (); + /** + * @brief The deep shape store also keeps the number of threads to allocate for the hierarchical processor + * + * This is a kind of hack, but it's convenient. + */ + void set_threads (int n); + + /** + * @brief Gets the number of threads + */ + int threads () const + { + return m_threads; + } + private: friend class DeepLayer; @@ -189,6 +233,7 @@ private: tl::stable_vector m_layouts; tl::stable_vector m_builders; layout_map_type m_layout_map; + int m_threads; struct DeliveryMappingCacheKey { diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 12df8eeee..6aa47528a 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -720,6 +720,12 @@ int td_simple (); int po_any (); Class decl_DeepShapeStore ("db", "DeepShapeStore", + method ("threads=", &db::DeepShapeStore::set_threads, gsi::arg ("threads"), + "@brief Sets the number of threads for use for operations acting on this heap\n" + ) + + method ("threads", &db::DeepShapeStore::threads, + "@brief Gets the number of threads for use for operations acting on this heap\n" + ) + method ("instance_count", db::DeepShapeStore::instance_count, "@hide"), "@brief An opaque layout heap for the deep region processor\n" "\n" @@ -735,6 +741,9 @@ Class decl_DeepShapeStore ("db", "DeepShapeStore", "region = RBA::Region::new(cell.begin(layer), dss)\n" "@/code\n" "\n" + "The DeepShapeStore object also supplies some configuration options " + "for the operations acting on the deep regions. See for example \\threads=.\n" + "\n" "This class has been introduced in version 0.26.\n" ); diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index a5e582579..9a15f52d0 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -108,6 +108,66 @@ TEST(2) } CHECKPOINT(); - db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au1.gds"); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au2.gds"); +} + +TEST(3_BoolAndNot) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42), dss); + db::Region box (db::Box (2000, -1000, 6000, 4000)); + + db::Region r2minus3 = r2 - r3; + db::Region r2minusbox = r2 - box; + db::Region r2minus42 = r2 - r42; + db::Region rboxminus3 = box - r3; + db::Region r42minus3 = r42 - r3; + db::Region r42minus42 = r42 - r42; + + db::Region r2and3 = r2 & r3; + db::Region r2andbox = r2 & box; + db::Region r2and42 = r2 & r42; + db::Region rboxand3 = box & r3; + db::Region r42and3 = r42 & r3; + db::Region r42and42 = r42 & r42; + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2minus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2minusbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2minus42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rboxminus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r42minus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r42minus42); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2andbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r2and42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), rboxand3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r42and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r42and42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au3.gds"); } diff --git a/testdata/algo/deep_region_au1.gds b/testdata/algo/deep_region_au1.gds index bfa6b5bde37c2c7396658552e9a72212db726a82..c21eadd76136e63aee6e508001c0f1ec507796df 100644 GIT binary patch delta 507 zcmZ4F(Iuk9z{bGD6u}_F$i)7Lftx{)fssLiK?<2YQAt%8i}2=cJZBgu2e2qi?9i6R zBG16V#^&i4W;D4)(s8o3oa^KT!fl(R`xv=cD;O9U4>2&XR!p8DW5x#J@@$&?OVo^u z?*>Sefr0PFWGk^XlVb$F8e%h;i;XSF)6d&y+0|1Fqa^wI2 literal 9250 zcmdT|&x@5+7(VyT%)NJhD3cBb;X-8EiRq}5A&HhzgEGl9NQlrs(87gCgvLNEs+EgY z)UWzq z&*Sx8ulomn3$d<^*XiS&XE_i1&Qm`}$=%s1eazkM?K8ZN-(BvX;o<(czq6wK9OwRq zjd|+fKSRlr{afMr*KXVS*KR}a5}s#;k`p8Q1AXlMyB`Nq5BGmUD0xA@5Hs{&Ss^f47a!?^buHR&@$$qV`gpDEA1BJ$ku3FP^Kb@=RV z<};75^g4Dh5KiSb2HDKQ1W>Hz~@QKz%kdOi z9o#xj7I=MQ)g7yEsyb(O7OHbDPwWr8juBL4{|Ah#b1uhIc>cs|oQG&BFnTVR{($OS z^xFP%ufftQ*pb|K1^)$3?~jL82VU!s+dAS>p#jmhg1nlOvi-p+yfeRV z!YN!=^=0TYS7tZQlxKJ281_fJG>%D5jG{Y;6O4hs#n;30IQ4PTP4<2fC%|rh_q0Fj z50X0%?8i9+PVjNc^D|z7syfwi8goM0`whzpjBkJc9$`OXA?q`I%nk7QL{`&Li#qmZ zk<@Xl7jK zWMRi<_C3-L-xT?F-@OIPfxRD*-RRtel6P-KCrb38bCUWv(HqpyQF3>(UVr?C=}&xP z`a;RaZ`A8g9x?r?L#8j3eDa9t54b1vGX6&UA1C(D&gGRW>YyH;LpfXS^@we}xBY!B zN6A~uk=32>)*pQeOVNQ58+S}!C^;sCm7}0PzSs08Z<@YP^6|Zqek0w#-6_|XyxocP z$3p#wU*r5&aeiPM-)bIxl%wRWtENBvt@BINgmXK#WnZTH4q>!U@tailJnzre$IO|+ z%mewniTgG44!YJ0jTOh)@Pu_a{?G-;5L3kQ3+MyB$Wd}ksMcSF4F~z& zhDl$+F7$dr$w%<*h2QzI^F}$b_5iD6e`1HhrA#_W8FX*)5d3&i*u~8Dq?C;_x{QzkPtS+%e?&E$1WqYjWE3CT|)JpVP7a;GBl7 zyK#RX8jhX;f*XaB1H=2@a81bK=b2>^-aDDBj~TUr4>aHj@iZf z$9L2^Qv8BUC5D0#j9(`Po^?@?ng<}>|fC^<1Ye~{OvCkC7P!SscaSLe0qi@~=3YWhOS zmDgGP1B|;5|8F2IGZo%3gK4r8!(POWB9Ik=LLkLxVi7oD-}Q z=K8Z#QJzX~y|%&%g9@;c_te&7OHPb3AgW;0V+A6rClyLw*PE_AV1@YqRv@06Jv320 zwNUbe0ip`x?;jP2`syeQS$QZYM)#lS!*6(BramhVdvl@Wbrs0ljrV0DD-ZRBtUxv% z?Z4p)G@yduyU)_X;+t>xD@RgKkgF&Hryc)1qgpQ!%|aV#Z@e?t>gSRF-{O~ rh;cAv>zE;*pFeNUM9G0+#}nz#e{b`M)L-0{BlWX!ua9^c9d-WzeKT?l diff --git a/testdata/algo/deep_region_au2.gds b/testdata/algo/deep_region_au2.gds new file mode 100644 index 0000000000000000000000000000000000000000..bfa6b5bde37c2c7396658552e9a72212db726a82 GIT binary patch literal 9250 zcmdT|&x@5+7(VyT%)NJhD3cBb;X-8EiRq}5A&HhzgEGl9NQlrs(87gCgvLNEs+EgY z)UWzq z&*Sx8ulomn3$d<^*XiS&XE_i1&Qm`}$=%s1eazkM?K8ZN-(BvX;o<(czq6wK9OwRq zjd|+fKSRlr{afMr*KXVS*KR}a5}s#;k`p8Q1AXlMyB`Nq5BGmUD0xA@5Hs{&Ss^f47a!?^buHR&@$$qV`gpDEA1BJ$ku3FP^Kb@=RV z<};75^g4Dh5KiSb2HDKQ1W>Hz~@QKz%kdOi z9o#xj7I=MQ)g7yEsyb(O7OHbDPwWr8juBL4{|Ah#b1uhIc>cs|oQG&BFnTVR{($OS z^xFP%ufftQ*pb|K1^)$3?~jL82VU!s+dAS>p#jmhg1nlOvi-p+yfeRV z!YN!=^=0TYS7tZQlxKJ281_fJG>%D5jG{Y;6O4hs#n;30IQ4PTP4<2fC%|rh_q0Fj z50X0%?8i9+PVjNc^D|z7syfwi8goM0`whzpjBkJc9$`OXA?q`I%nk7QL{`&Li#qmZ zk<@Xl7jK zWMRi<_C3-L-xT?F-@OIPfxRD*-RRtel6P-KCrb38bCUWv(HqpyQF3>(UVr?C=}&xP z`a;RaZ`A8g9x?r?L#8j3eDa9t54b1vGX6&UA1C(D&gGRW>YyH;LpfXS^@we}xBY!B zN6A~uk=32>)*pQeOVNQ58+S}!C^;sCm7}0PzSs08Z<@YP^6|Zqek0w#-6_|XyxocP z$3p#wU*r5&aeiPM-)bIxl%wRWtENBvt@BINgmXK#WnZTH4q>!U@tailJnzre$IO|+ z%mewniTgG44!YJ0jTOh)@Pu_a{?G-;5L3kQ3+MyB$Wd}ksMcSF4F~z& zhDl$+F7$dr$w%<*h2QzI^F}$b_5iD6e`1HhrA#_W8FX*)5d3&i*u~8Dq?C;_x{QzkPtS+%e?&E$1WqYjWE3CT|)JpVP7a;GBl7 zyK#RX8jhX;f*XaB1H=2@a81bK=b2>^-aDDBj~TUr4>aHj@iZf z$9L2^Qv8BUC5D0#j9(`Po^?@?ng<}>|fC^<1Ye~{OvCkC7P!SscaSLe0qi@~=3YWhOS zmDgGP1B|;5|8F2IGZo%3gK4r8!(POWB9Ik=LLkLxVi7oD-}Q z=K8Z#QJzX~y|%&%g9@;c_te&7OHPb3AgW;0V+A6rClyLw*PE_AV1@YqRv@06Jv320 zwNUbe0ip`x?;jP2`syeQS$QZYM)#lS!*6(BramhVdvl@Wbrs0ljrV0DD-ZRBtUxv% z?Z4p)G@yduyU)_X;+t>xD@RgKkgF&Hryc)1qgpQ!%|aV#Z@e?t>gSRF-{O~ rh;cAv>zE;*pFeNUM9G0+#}nz#e{b`M)L-0{BlWX!ua9^c9d-WzeKT?l literal 0 HcmV?d00001 diff --git a/testdata/algo/deep_region_au3.gds b/testdata/algo/deep_region_au3.gds new file mode 100644 index 0000000000000000000000000000000000000000..e2e935ba883d674b6b0f5129915c00aeb11dca8e GIT binary patch literal 4074 zcmai%PiS0a6vglS>6^*SBu+D#Ng1jLB?Y09Vv8Y*h_L}pEu+K`LTN!Lbm1y1S1l9@ z1q%hCuBwZyDipdZ6cHD$vd~Re-MA?tUAW5HR-fPR-8sCON#2)DPI~S=-}&Bs_uki7 zV&(F-oh+4We_GRyTgl$E^P|68y_{TH|Hy2va_ocEZ9RE}nJJsunRX@oyqTu|Er4dFKg`N^h2k@;9zRr{rvH9T97LC< z>20C-1Nv6(KdGPeSKLPZ=SKDEZTE5C{5kx&xKHk1vuLI2NwPlJde5p2v&O8d)@oP6 z2|MBBPywoNc(k#9NHpn}ZkdG>LQTFcIAJ13=Nxg@@K!h02Oodt_o>hN%`>OWC%@}h ztnvBSEyiMVyT!4$_KRchd{`ViH~!RPek(7Eb)TFqj&0m3j$L_{$A;bB9PO4@FTGn{ zz4R`x_J!i}ZaJtA|^E;k5zvH?SKMTdj_5J6b*MIK1Q=u90 zKh+Q)*Dsv*yoJ-Qd-t+Xd|bbnO}p7*Htl9hk6gd>$o0ec=(LX1-)wo$^OpBqxB8Dz zeB6Jn=Xq;A*Il|O6d%_Qu6y3#y6d(c2*tE@`{97nKrcYD)ZmR3p`aKM3 zKdgr#il6F^ZM(7h(RU%9^&dL=Pbfa7r|-xE(b0cG@i9GpM;?fd{u7Fi>FGQ2Ky>t< zP<%{J-;oERqyL2BV|x0IJP;lIClnvk(|6>7=;%M8_?Vu)BM(GJ{|Uv%^zh%fln`G0hej)TyhJk+(&R!9k&9pr(to`CTA$;?i=YiE9!oSbt zSUK-;M?bf9RcHp^`YuQGt*fpFIe+$kj`&W$4tE$L_a^S>=XUN1UDth*Bj>mTkI?(x(08O6`iXtsyeR&_ zKAF&aKWGo2ryoH20e+$%#`IHL?lZOJzB6A5#mD{8;RE7}UMN21-@fBM?K|$<4fDYB zcOQt4`=i4L#239#e9XV|(0w`&-B;gbeW!=nJj=6CZe}q2FYc`+5xn&-+IULR<^Bm%Z{`42~2QruS zUvm^6uu#wQvv=4-mAC!*9mQuD(YrT#=8*acxf6WALOu1vpZeiT{T#)|{n6nA;;X#{ zijVov-uL&!9^<`r?g_=m-(UBM=ds7ovzHvjAEDPC44Cb=uj{M4Q2cN8D5KRW6RQh)S9@v-_-Km4=rK=7x2`0G0`j2AvXI($HU P^<4nP%Rl@@SFPIro;b=; literal 0 HcmV?d00001 From fc729fc830ede011a6779760c4c5f5256d83a763 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 18 Nov 2018 09:58:10 +0100 Subject: [PATCH 060/335] WIP: DeepRegion::add implemented. --- src/db/db/dbDeepRegion.cc | 71 ++++++++++++++++++++++++- src/db/db/dbDeepRegion.h | 8 +++ src/db/db/dbDeepShapeStore.cc | 13 +++++ src/db/db/dbDeepShapeStore.h | 5 ++ src/db/unit_tests/dbDeepRegionTests.cc | 70 ++++++++++++++++++++++++ testdata/algo/deep_region_au4a.gds | Bin 0 -> 2266 bytes testdata/algo/deep_region_au4b.gds | Bin 0 -> 9258 bytes 7 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 testdata/algo/deep_region_au4a.gds create mode 100644 testdata/algo/deep_region_au4b.gds diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index b14e1818c..735cd41c0 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -28,6 +28,8 @@ #include "dbShapeProcessor.h" #include "dbFlatRegion.h" #include "dbHierProcessor.h" +#include "dbCellMapping.h" +#include "dbLayoutUtils.h" namespace db { @@ -125,7 +127,7 @@ DeepRegion::~DeepRegion () DeepRegion::DeepRegion (const DeepRegion &other) : AsIfFlatRegion (other), - m_deep_layer (other.m_deep_layer), + m_deep_layer (other.m_deep_layer.copy ()), m_merged_polygons (other.m_merged_polygons), m_merged_polygons_valid (other.m_merged_polygons_valid) { @@ -354,5 +356,72 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const return dl_out; } +RegionDelegate * +DeepRegion::add_in_place (const Region &other) +{ + if (other.empty ()) { + return this; + } + + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + if (other_deep) { + + if (other_deep->deep_layer ().layout () == deep_layer ().layout ()) { + + // intra-layout merge + + deep_layer ().layout ()->copy_layer (other_deep->deep_layer ().layer (), deep_layer ().layer ()); + + } else { + + // inter-layout merge + + db::cell_index_type into_cell = deep_layer ().initial_cell ()->cell_index (); + db::Layout *into_layout = deep_layer ().layout (); + db::cell_index_type source_cell = other_deep->deep_layer ().initial_cell ()->cell_index (); + const db::Layout *source_layout = other_deep->deep_layer ().layout (); + + db::CellMapping cm; + cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell); + + // Actually copy the shapes + + std::map lm; + lm.insert (std::make_pair (other_deep->deep_layer ().layer (), deep_layer ().layer ())); + + std::vector source_cells; + source_cells.push_back (source_cell); + db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cm.table (), lm); + + } + + } else { + + // non-deep to deep merge (flat) + + db::Shapes &shapes = deep_layer ().initial_cell ()->shapes (deep_layer ().layer ()); + for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { + shapes.insert (*p); + } + + } + + return this; + } +RegionDelegate * +DeepRegion::add (const Region &other) const +{ + if (other.empty ()) { + return clone (); + } else if (empty ()) { + return other.delegate ()->clone (); + } else { + DeepRegion *new_region = dynamic_cast (clone ()); + new_region->add_in_place (other); + return new_region; + } +} + +} diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index a69e6ff7f..9cdd7df97 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -73,6 +73,9 @@ public: virtual RegionDelegate *and_with (const Region &other) const; virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *add_in_place (const Region &other); + virtual RegionDelegate *add (const Region &other) const; + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; const DeepLayer &deep_layer () const @@ -80,6 +83,11 @@ public: return m_deep_layer; } + DeepLayer &deep_layer () + { + return m_deep_layer; + } + protected: virtual void merged_semantics_changed (); diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index ed76dbaf5..51d275f23 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -61,6 +61,19 @@ DeepLayer::derived () const return DeepLayer (const_cast (mp_store.get ()), m_layout, const_cast (layout ())->insert_layer ()); } +DeepLayer +DeepLayer::copy () const +{ + DeepLayer new_layer (derived ()); + + db::DeepShapeStore *non_const_store = const_cast (mp_store.get ()); + if (non_const_store->layout (m_layout)) { + non_const_store->layout (m_layout)->copy_layer (m_layer, new_layer.layer ()); + } + + return new_layer; +} + void DeepLayer::insert_into (db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) const { diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index e920ad788..9607815b0 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -119,6 +119,11 @@ public: */ DeepLayer derived () const; + /** + * @brief Creates a copy of this layer + */ + DeepLayer copy () const; + /** * @brief Gets the shape store object * This is a pure const version to prevent manipulation of the store. diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 9a15f52d0..e1a76ecbb 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -171,3 +171,73 @@ TEST(3_BoolAndNot) db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au3.gds"); } +TEST(4_Add) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42), dss); + db::Region box (db::Box (2000, -1000, 6000, 4000)); + db::Region r2box (db::RecursiveShapeIterator (ly, top_cell, l2, box), dss); + db::Region r3box (db::RecursiveShapeIterator (ly, top_cell, l3, box), dss); + + // intra-layout + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2 + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r42 + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2 + r42); + + db::Region rnew2 = r2; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), rnew2); + rnew2 += r3; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), rnew2); + rnew2 += r42; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), rnew2); + + db::Region rnew42 = r42; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), rnew42); + rnew42 += r2; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), rnew42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au4a.gds"); + } + + // inter-layout + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2box + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2 + r3box); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2box + r3box); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), box + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2 + box); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au4b.gds"); + } +} diff --git a/testdata/algo/deep_region_au4a.gds b/testdata/algo/deep_region_au4a.gds new file mode 100644 index 0000000000000000000000000000000000000000..d87d595f7629c7e8dc5f1aafb45a23e7451cf67a GIT binary patch literal 2266 zcmb`Ize_?<6vw|lpV!w@qs$s2`Y|*FK~X_86d6KEVPO&x{t-=Wf#6hAdkt>=3k}T; zP3<*EdmuOk>Fqr2;Jx8Byf%I1eEr<}zRz=x5OADROog22J7P#;2y;lPpW!*;AiD#k z!jY}?)%yO^eQhK2uvED{D_|;O=6oRv`E>3Wa6LdI9QY8!4uHjxu#}ApDVy_w0-;yH zxn|{hpm0YGyev!kRh3eff$%JyddbT5Ks7$5-fzLjRo>$HQv>qj|H)hApBV6djkmae z#`gXwdUmQOL^gM_0@nxpBzJ8*=+fpnbryAC(1qPyzt+VSlc5qoCs0q{7*6>09X_Y) ztkkK`1?dv=wth>vT|J%G+vw4M*sO0-YUPwTjW>xc5cqexmmgXE5APL{pVMBtNa>o zkze7h@@u=o;`dj0tNa>okze8a%m2r&qJPzVHn)4&-bY@ty}4SOrnP6N{lulUmO=nc9qz5xfBJGuY> literal 0 HcmV?d00001 diff --git a/testdata/algo/deep_region_au4b.gds b/testdata/algo/deep_region_au4b.gds new file mode 100644 index 0000000000000000000000000000000000000000..8947f5e0210867cb03595ccf892d1296083ad5f9 GIT binary patch literal 9258 zcmeI2Uuc$P7{;Id@!Qta^XvY7_jmdJ@q3?d`{>Z4Yh2HBz4v=x*ZchW ztWXNg&F_Z(rslT4LTBg+Ys2o)m;O7nHr5K8NstwyQ4IfO8@zNK6)Uo4mJ-vL8t88G&|P&^n~ zpEP}2h*s-*SS_j5(P*t&@49@hTz>FExx9QXgr+}3X#Pwn9`tvu>ig35SEH}fYp;5p zI5IhP>e%?S87zM@J?CpSjrlt_jTOFgSL~f*`a192QGMrvbnepm@WISGC^%-;=h9&6dkx+jaL=D84l7`Z}4}8YZ*B z)Do`b(^1W@c6v2N<+o+qtL#$`&83t25>_xf_3UmvyRw_t`mzdU6h5Q)&9YC*&C~O0 z?$N9NzEbB=L*G|M-@x~k())L%??~U3P`tjcYX6x%WqI}er}Pc=pV2qae@g%V^v`{Q z{iS8V_ZLUMPvDR46Se)bb$!$I-R$qSKMBRR-OTI{srPsP?D>`biTe!ulVhcBW&bL! z?>c*aMc-}b3VNY<)vvR^*eRCTUwrrP(JIysQS*$5%+l(!-6q+Kaw{~o#}v*$)$D%bJR{Oolq-0xl(%jqA&Q#1!_kTva|Sj(U;AN;AHicvBH!4k(FU5`)W?^ zjJ|>XQ+oC1sJ?|O?f>!4!f(T1H~HzwqX*x9xTECu8C$`&IZW9%Ycs01PP=PlX7P)# zGOu?bGq2s}`B-Hnc}8X|_x#LQ?3vFj71w)dtaxna<>Ik@cZ$c}e6@IN`a<#8$(zYo z^`z=eewq0xb$=5W!V?Q2JatBBCryQ~VEQJT(l@l8Q+n31Zrr+o3&lTj6VK=ySkEba zLpOzt-fsfFpRMqwkkhaFrjXLRf2+JHo^^eZAm?w44m|CXzqXEw*M?YhY^8lkJay)_ndm> ztq1!hz2TBjd_2FtYd){Tx|FQLJIrkZo|Hi))6d%if-F?p=B>xTOfsp*G z__+V*@EOE+^g{75|M8Fd_(xs*a}*!1kB&MZbG#^R~^nZu?t8@pbh5)~{v0&5+!;f0Cg1SpLtOhMYn2-}+sG;tdP)r+@sX ze|)EZNAYq0(cv?Q@92f%WB%hG_3@9o_~$4-ULPHGK>9gw={h2mrN_x$cT?fKnv+xwePd_2F= zdp>XUp6gz}D->TxZ<&$vs3G}}4JIf)mjCX(oYm_PmFKmFr7{X2?}`;QKv zL3~Fq6d&^+|EQ0D)WttX@$vfTr~^_Ly-<9t{?u8|Y3i)!_Rfbw@$vj-=6&AGyz6E! z3B}jZpR|50{~1H_pZzI8@v;05-}3xH@}F8tko>FTh56Gz{?k9c)4!wmxc}(z8N_$= zLh&*G@sIlWM_v4L6d$jTjyfQ9(F?`L>L0)1IUT>@xt;hyC_bLw+@jB$TXfxfw}s;C z=&xG8mj9d~`Jd`dP<$-^V?&-lNd70r5+wiXcwzqZkN@ Date: Wed, 21 Nov 2018 00:17:14 +0100 Subject: [PATCH 061/335] WIP: added ref counting for deep shape layers and layouts --- src/db/db/dbDeepShapeStore.cc | 131 +++++++++++++++--- src/db/db/dbDeepShapeStore.h | 35 ++++- src/db/unit_tests/dbDeepShapeStoreTests.cc | 151 +++++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 3 +- 4 files changed, 294 insertions(+), 26 deletions(-) create mode 100644 src/db/unit_tests/dbDeepShapeStoreTests.cc diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 51d275f23..41f3909b9 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -41,18 +41,42 @@ DeepLayer::DeepLayer () DeepLayer::DeepLayer (const DeepLayer &x) : mp_store (x.mp_store), m_layout (x.m_layout), m_layer (x.m_layer) { - // .. nothing yet .. + if (mp_store.get ()) { + mp_store->add_ref (m_layout, m_layer); + } } DeepLayer::DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer) : mp_store (store), m_layout (layout), m_layer (layer) { - // .. nothing yet .. + if (store) { + store->add_ref (layout, layer); + } } +DeepLayer &DeepLayer::operator= (const DeepLayer &other) +{ + if (this != &other) { + if (mp_store.get ()) { + mp_store->remove_ref (m_layout, m_layer); + } + mp_store = other.mp_store; + m_layout = other.m_layout; + m_layer = other.m_layer; + if (mp_store.get ()) { + mp_store->add_ref (m_layout, m_layer); + } + } + + return *this; +} + + DeepLayer::~DeepLayer () { - // .. nothing yet .. + if (mp_store.get ()) { + mp_store->remove_ref (m_layout, m_layer); + } } DeepLayer @@ -121,6 +145,35 @@ DeepLayer::check_dss () const // ---------------------------------------------------------------------------------- +struct DeepShapeStore::LayoutHolder +{ + LayoutHolder () + : refs (0), layout (), builder (&layout) + { + // .. nothing yet .. + } + + void add_layer_ref (unsigned int layer) + { + layer_refs[layer] += 1; + } + + void remove_layer_ref (unsigned int layer) + { + if ((layer_refs[layer] -= 1) <= 0) { + layout.clear_layer (layer); + layer_refs.erase (layer); + } + } + + int refs; + db::Layout layout; + db::HierarchyBuilder builder; + std::map layer_refs; +}; + +// ---------------------------------------------------------------------------------- + static size_t s_instance_count = 0; DeepShapeStore::DeepShapeStore () @@ -132,6 +185,28 @@ DeepShapeStore::DeepShapeStore () DeepShapeStore::~DeepShapeStore () { --s_instance_count; + + for (std::vector::iterator h = m_layouts.begin (); h != m_layouts.end (); ++h) { + delete *h; + } + m_layouts.clear (); +} + +bool DeepShapeStore::is_valid_layout_index (unsigned int n) const +{ + return (n < (unsigned int) m_layouts.size () && m_layouts[n] != 0); +} + +const db::Layout *DeepShapeStore::const_layout (unsigned int n) const +{ + tl_assert (is_valid_layout_index (n)); + return &(m_layouts [n]->layout); +} + +db::Layout *DeepShapeStore::layout (unsigned int n) +{ + tl_assert (is_valid_layout_index (n)); + return &(m_layouts [n]->layout); } size_t DeepShapeStore::instance_count () @@ -144,35 +219,55 @@ void DeepShapeStore::set_threads (int n) m_threads = n; } +void DeepShapeStore::add_ref (unsigned int layout, unsigned int layer) +{ + tl::MutexLocker locker (&m_lock); + + tl_assert (layout < (unsigned int) m_layouts.size () && m_layouts[layout] != 0); + + m_layouts[layout]->refs += 1; + m_layouts[layout]->add_layer_ref (layer); +} + +void DeepShapeStore::remove_ref (unsigned int layout, unsigned int layer) +{ + tl::MutexLocker locker (&m_lock); + + tl_assert (layout < (unsigned int) m_layouts.size () && m_layouts[layout] != 0); + + m_layouts[layout]->remove_layer_ref (layer); + + if ((m_layouts[layout]->refs -= 1) <= 0) { + delete m_layouts[layout]; + m_layouts[layout] = 0; + } +} + DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count) { unsigned int layout_index = 0; - unsigned int layer_index = 0; layout_map_type::iterator l = m_layout_map.find (si); if (l == m_layout_map.end ()) { layout_index = (unsigned int) m_layouts.size (); - m_layouts.push_back (new db::Layout ()); - m_layouts.back ().dbu (si.layout ()->dbu ()); - layer_index = m_layouts.back ().insert_layer (); - - m_builders.push_back (new db::HierarchyBuilder (&m_layouts.back (), layer_index)); + m_layouts.push_back (new LayoutHolder ()); + m_layouts.back ()->layout.dbu (si.layout ()->dbu ()); m_layout_map[si] = layout_index; } else { layout_index = l->second; - layer_index = m_layouts[layout_index].insert_layer (); - - m_builders[layout_index].set_target_layer (layer_index); } + unsigned int layer_index = m_layouts[layout_index]->layout.insert_layer (); + m_layouts[layout_index]->builder.set_target_layer (layer_index); + // The chain of operators for producing clipped and reduced polygon references - db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& m_layouts[layout_index]); + db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& m_layouts[layout_index]->layout); db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count); db::ClippingHierarchyBuilderShapeReceiver clip (&red); @@ -181,12 +276,12 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator tl::SelfTimer timer (tl::to_string (tr ("Building working hierarchy"))); - m_builders[layout_index].set_shape_receiver (&clip); - db::RecursiveShapeIterator (si).push (& m_builders[layout_index]); - m_builders[layout_index].set_shape_receiver (0); + m_layouts[layout_index]->builder.set_shape_receiver (&clip); + db::RecursiveShapeIterator (si).push (& m_layouts[layout_index]->builder); + m_layouts[layout_index]->builder.set_shape_receiver (0); } catch (...) { - m_builders[layout_index].set_shape_receiver (0); + m_layouts[layout_index]->builder.set_shape_receiver (0); throw; } @@ -204,7 +299,7 @@ DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db db::cell_index_type source_top = *source_layout->begin_top_down(); - db::HierarchyBuilder &original_builder = m_builders [deep_layer.layout_index ()]; + db::HierarchyBuilder &original_builder = m_layouts [deep_layer.layout_index ()]->builder; // derive a cell mapping for source to target. We employ a diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 9607815b0..c4ee8cd0c 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -28,6 +28,7 @@ #include "tlObject.h" #include "tlStableVector.h" +#include "tlThreads.h" #include "dbLayout.h" #include "dbRecursiveShapeIterator.h" #include "dbHierarchyBuilder.h" @@ -197,7 +198,7 @@ public: DeepLayer create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio = 3.0, size_t max_vertex_count = 16); /** - * @brief Inserts the deep layer's into some target layout + * @brief Inserts the deep layer's shapes into some target layout */ void insert (const DeepLayer &layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer); @@ -206,6 +207,24 @@ public: */ static size_t instance_count (); + /** + * @brief Gets the nth layout (const version) + */ + const db::Layout *const_layout (unsigned int n) const; + + /** + * @brief Gets the number of layouts + */ + unsigned int layouts () const + { + return (unsigned int) m_layouts.size (); + } + + /** + * @brief Gets a value indicating whether the given index is a valid layout index + */ + bool is_valid_layout_index (unsigned int n) const; + /** * @brief The deep shape store also keeps the number of threads to allocate for the hierarchical processor * @@ -224,10 +243,12 @@ public: private: friend class DeepLayer; - db::Layout *layout (unsigned int n) - { - return &m_layouts [n]; - } + struct LayoutHolder; + + db::Layout *layout (unsigned int n); + + void add_ref (unsigned int layout, unsigned int layer); + void remove_ref (unsigned int layout, unsigned int layer); typedef std::map layout_map_type; @@ -235,10 +256,10 @@ private: DeepShapeStore (const DeepShapeStore &); DeepShapeStore &operator= (const DeepShapeStore &); - tl::stable_vector m_layouts; - tl::stable_vector m_builders; + std::vector m_layouts; layout_map_type m_layout_map; int m_threads; + tl::Mutex m_lock; struct DeliveryMappingCacheKey { diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc new file mode 100644 index 000000000..eb21e8020 --- /dev/null +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -0,0 +1,151 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbDeepShapeStore.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1) +{ + db::DeepShapeStore store; + db::Layout layout; + + unsigned int l1 = layout.insert_layer (); + unsigned int l2 = layout.insert_layer (); + db::cell_index_type c1 = layout.add_cell ("C1"); + db::cell_index_type c2 = layout.add_cell ("C2"); + + EXPECT_EQ (store.layouts (), (unsigned int) 0); + + db::DeepLayer dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + db::DeepLayer dl2 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l2)); + + EXPECT_EQ (dl1.layer (), l1); + EXPECT_EQ (dl2.layer (), l2); + EXPECT_EQ (dl1.layout (), dl2.layout ()); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + db::DeepLayer dl3 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c2), l1)); + EXPECT_EQ (dl3.layer (), l1); + EXPECT_NE (dl1.layout (), dl3.layout ()); + EXPECT_EQ (store.layouts (), (unsigned int) 2); + + db::DeepLayer dl4 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1, db::Box (0, 1, 2, 3))); + db::DeepLayer dl5 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l2, db::Box (0, 1, 2, 3))); + EXPECT_EQ (dl4.layer (), l1); + EXPECT_EQ (dl5.layer (), l1); // not l2, because it's a new layout + EXPECT_EQ (store.layouts (), (unsigned int) 4); + + db::DeepLayer dl6 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1, db::Box (0, 1, 2, 3))); + EXPECT_EQ (dl6.layer (), l2); // a new layer (a copy) + EXPECT_EQ (dl6.layout (), dl4.layout ()); + EXPECT_EQ (store.layouts (), (unsigned int) 4); +} + +static size_t shapes_in_top (const db::Layout *layout, unsigned int layer) +{ + const db::Cell &top = layout->cell (*layout->begin_top_down ()); + return top.shapes (layer).size (); +} + +TEST(2_RefCounting) +{ + db::DeepShapeStore store; + db::Layout layout; + + unsigned int l1 = layout.insert_layer (); + unsigned int l2 = layout.insert_layer (); + db::cell_index_type c1 = layout.add_cell ("C1"); + db::cell_index_type c2 = layout.add_cell ("C2"); + layout.cell (c1).shapes (l1).insert (db::Box (0, 1, 2, 3)); + layout.cell (c1).shapes (l2).insert (db::Box (0, 1, 2, 3)); + + EXPECT_EQ (store.layouts (), (unsigned int) 0); + + db::DeepLayer dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + db::DeepLayer dl2 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l2)); + db::DeepLayer dl3 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c2), l1)); + db::DeepLayer dl4 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1, db::Box (0, 1, 2, 3))); + db::DeepLayer dl5 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l2, db::Box (0, 1, 2, 3))); + db::DeepLayer dl6 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1, db::Box (0, 1, 2, 3))); + + EXPECT_EQ (store.layouts (), (unsigned int) 4); + + unsigned int lyi1 = dl1.layout_index (); + unsigned int lyi2 = dl2.layout_index (); + unsigned int lyi3 = dl3.layout_index (); + unsigned int lyi4 = dl4.layout_index (); + unsigned int lyi5 = dl5.layout_index (); + unsigned int lyi6 = dl6.layout_index (); + + EXPECT_EQ (lyi1, lyi2); + EXPECT_NE (lyi3, lyi2); + EXPECT_NE (lyi5, lyi4); + EXPECT_NE (lyi5, lyi3); + EXPECT_EQ (lyi6, lyi4); + + EXPECT_EQ (dl1.layer (), l1); + EXPECT_EQ (dl2.layer (), l2); + EXPECT_EQ (dl4.layer (), l1); + EXPECT_EQ (dl6.layer (), l2); + + // dl1 and dl2 share the same layout, but not the same layer + // dl4 and dl6 share the same layout, but not the same layer + + EXPECT_EQ (store.is_valid_layout_index (lyi6), true); + EXPECT_EQ (store.is_valid_layout_index (lyi5), true); + EXPECT_EQ (store.is_valid_layout_index (lyi3), true); + EXPECT_EQ (store.is_valid_layout_index (lyi1), true); + + EXPECT_EQ (shapes_in_top (store.const_layout (lyi6), l2), size_t (1)); + dl6 = db::DeepLayer (); + EXPECT_EQ (shapes_in_top (store.const_layout (lyi6), l2), size_t (0)); + + EXPECT_EQ (shapes_in_top (store.const_layout (lyi6), l1), size_t (1)); + db::DeepLayer dl4a = dl4; + dl4 = db::DeepLayer (); + EXPECT_EQ (shapes_in_top (store.const_layout (lyi6), l1), size_t (1)); + dl4a = db::DeepLayer (); + EXPECT_EQ (store.is_valid_layout_index (lyi6), false); + + dl3 = db::DeepLayer (); + EXPECT_EQ (store.is_valid_layout_index (lyi3), false); + + { + db::DeepLayer dl5a = dl5; + db::DeepLayer dl5b = dl5a; + dl5 = db::DeepLayer (); + EXPECT_EQ (store.is_valid_layout_index (lyi5), true); + } + EXPECT_EQ (store.is_valid_layout_index (lyi5), false); + + EXPECT_EQ (shapes_in_top (store.const_layout (lyi1), l1), size_t (1)); + EXPECT_EQ (shapes_in_top (store.const_layout (lyi1), l2), size_t (1)); + + dl1 = db::DeepLayer (); + EXPECT_EQ (shapes_in_top (store.const_layout (lyi1), l1), size_t (0)); + EXPECT_EQ (shapes_in_top (store.const_layout (lyi1), l2), size_t (1)); + + dl2 = db::DeepLayer (); + EXPECT_EQ (store.is_valid_layout_index (lyi1), false); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 5c0d7a9a4..b35a4ef9e 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -56,7 +56,8 @@ SOURCES = \ dbHierarchyBuilderTests.cc \ dbRecursiveShapeIteratorTests.cc \ dbHierProcessorTests.cc \ - dbDeepRegionTests.cc + dbDeepRegionTests.cc \ + dbDeepShapeStoreTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC From 06c11d009693a2eee4bad2a058609493ecd3ca76 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 21 Nov 2018 00:32:14 +0100 Subject: [PATCH 062/335] WIP: Deep region XOR implemented. --- src/db/db/dbDeepRegion.cc | 98 +++++++++++++++++-------- src/db/db/dbDeepRegion.h | 2 + src/db/unit_tests/dbDeepRegionTests.cc | 47 ++++++++++++ testdata/algo/deep_region_au5.gds | Bin 0 -> 5066 bytes 4 files changed, 118 insertions(+), 29 deletions(-) create mode 100644 testdata/algo/deep_region_au5.gds diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 735cd41c0..1210f81b1 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -325,7 +325,7 @@ DeepRegion::not_with (const Region &other) const // Nothing to do return new EmptyRegion (); - } else if (other.empty () && ! strict_handling ()) { + } else if (other.empty ()) { // Nothing to do return clone (); @@ -356,6 +356,73 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const return dl_out; } +RegionDelegate * +DeepRegion::xor_with (const Region &other) const +{ + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return other.delegate ()->clone (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatRegion::xor_with (other); + + } else { + + // Implement XOR as (A-B)+(B-A) - only this implementation + // is compatible with the local processor scheme + DeepLayer n1 (and_or_not_with (other_deep, false)); + DeepLayer n2 (other_deep->and_or_not_with (this, false)); + + std::auto_ptr r (new DeepRegion (n1)); + r->add_from (n2); + return r.release (); + + } +} + +void +DeepRegion::add_from (const DeepLayer &dl) +{ + if (dl.layout () == deep_layer ().layout ()) { + + // intra-layout merge + + deep_layer ().layout ()->copy_layer (dl.layer (), deep_layer ().layer ()); + + } else { + + // inter-layout merge + + db::cell_index_type into_cell = deep_layer ().initial_cell ()->cell_index (); + db::Layout *into_layout = deep_layer ().layout (); + db::cell_index_type source_cell = dl.initial_cell ()->cell_index (); + const db::Layout *source_layout = dl.layout (); + + db::CellMapping cm; + cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell); + + // Actually copy the shapes + + std::map lm; + lm.insert (std::make_pair (dl.layer (), deep_layer ().layer ())); + + std::vector source_cells; + source_cells.push_back (source_cell); + db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cm.table (), lm); + + } +} + + RegionDelegate * DeepRegion::add_in_place (const Region &other) { @@ -366,34 +433,7 @@ DeepRegion::add_in_place (const Region &other) const DeepRegion *other_deep = dynamic_cast (other.delegate ()); if (other_deep) { - if (other_deep->deep_layer ().layout () == deep_layer ().layout ()) { - - // intra-layout merge - - deep_layer ().layout ()->copy_layer (other_deep->deep_layer ().layer (), deep_layer ().layer ()); - - } else { - - // inter-layout merge - - db::cell_index_type into_cell = deep_layer ().initial_cell ()->cell_index (); - db::Layout *into_layout = deep_layer ().layout (); - db::cell_index_type source_cell = other_deep->deep_layer ().initial_cell ()->cell_index (); - const db::Layout *source_layout = other_deep->deep_layer ().layout (); - - db::CellMapping cm; - cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell); - - // Actually copy the shapes - - std::map lm; - lm.insert (std::make_pair (other_deep->deep_layer ().layer (), deep_layer ().layer ())); - - std::vector source_cells; - source_cells.push_back (source_cell); - db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cm.table (), lm); - - } + add_from (other_deep->deep_layer ()); } else { diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 9cdd7df97..8f6c8a909 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -72,6 +72,7 @@ public: virtual RegionDelegate *and_with (const Region &other) const; virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *xor_with (const Region &other) const; virtual RegionDelegate *add_in_place (const Region &other); virtual RegionDelegate *add (const Region &other) const; @@ -103,6 +104,7 @@ private: void init (); void ensure_merged_polygons_valid () const; DeepLayer and_or_not_with(const DeepRegion *other, bool and_op) const; + void add_from (const DeepLayer &dl); }; } diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index e1a76ecbb..41b148e58 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -241,3 +241,50 @@ TEST(4_Add) db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au4b.gds"); } } + +TEST(5_BoolXOR) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42), dss); + db::Region box (db::Box (2000, -1000, 6000, 4000)); + + db::Region r2xor3 = r2 ^ r3; + db::Region r2xorbox = r2 ^ box; + db::Region r2xor42 = r2 ^ r42; + db::Region rboxxor3 = box ^ r3; + db::Region r42xor3 = r42 ^ r3; + db::Region r42xor42 = r42 ^ r42; + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2xor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2xorbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2xor42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rboxxor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r42xor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r42xor42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au5.gds"); +} + diff --git a/testdata/algo/deep_region_au5.gds b/testdata/algo/deep_region_au5.gds new file mode 100644 index 0000000000000000000000000000000000000000..273f7e03e575db55d41544a223d316edd486e44d GIT binary patch literal 5066 zcmb7{UuczO9LJw?c6@g{J6lWLQN~uQH8q_qbvlOIa&2gvYt5Y@GPeo>FGN8wx=Iih zL6H#HRT&6g1d>;QMCg^_g(V0^Hz`O9FCszMRq1+rzR&l3dCt4e^Hw*00RBO{x0-CZCNa#$GXIUEQjo;Sc)`-1zcQ|L8Y87e1ez zF>R~DzCskuOb<_%jmhPWi5jz}V4|k1t5;cRL$e{C-)QnqMv_d|10NLJmGJu z_WSxP`~PSCdXu_S`6wEjJha2)3dR)HXxEC(b#}6o_Q}{Cz%JB|79Q1zMy;7M#@GyY zPd*7`Sg}TVYMqtLTAmFsf73pzMo*lbN<6q%%S${PVE(3^>Y*q0n(@T5PM+1f_8Iru zIV^MMZT(SC>6XFzf6Un*z)T#2^;h{+Md{H@!k^gdQ@Opi_Of1T$szlJB*oiumOtX?^*iCAGOaS9W$br-_1 zXC8%PFYXP;b|z1KqTb;Bu`5AFKR=8miwGk00PI6~~Vs@RyWp6{UlT z6IA6lru8Qmx1_Jrz6|io>nuo5_jhgNx~`4%ZJQOPr~7-{+uoQS_qI2t?-Koem*}s= ztABxs{`+rmUH=XGhW`|$r`I1XaouQ%zKL;Bdb)q|DA!FMrSH&ZqV#nC;VWEs_zHb* z{U%CJ^K;)v zMd-*cLSKK9aZo<-sSAvY&^LU`b>K=r=7LVDZ%;iJ%j)+>W99uhfqM|Xz_{(YgUx$5$n6JauLSN7R;EDdBi^$ut z%={qq=d4GBA3CYL!TN5#O*^@6(@xIyyv>O@LCghRME%X%Sby_2`GWb~d|S#~x24R! zwzPSoZ|EZOZ+(sVLFilG_rzS#N#zaJck^vu;JWP#oU8kHPs{~fMExD7S$~K9D-8Qm z@z*;4Zob}&T-SS%ef1vqME}r5+f%uFPPuWH#Wj`V^|! z&wS^JxuBEE8?5i9vi1zkk_BiC5}$Tj(b`Q3c;i(EIq$iC*k@*i Date: Thu, 22 Nov 2018 01:26:03 +0100 Subject: [PATCH 063/335] WIP: More on deep regions & hier processor - Splitting of shapes on output of booleans - A bugfix: error happened when pulling intruders from second-next hier level - Tests added - Some TODO comments added --- src/db/db/dbDeepRegion.cc | 2 +- src/db/db/dbDeepShapeStore.cc | 19 +++++++- src/db/db/dbDeepShapeStore.h | 38 +++++++++++++++- src/db/db/dbHierProcessor.cc | 36 ++++++++------- src/db/db/dbHierarchyBuilder.cc | 12 +---- src/db/db/dbLocalOperation.cc | 42 ++++++++++++++++-- src/db/db/dbLocalOperation.h | 4 +- src/db/db/dbPolygon.h | 26 ++++++++++- src/db/unit_tests/dbDeepRegionTests.cc | 51 ++++++++++++++++++++++ src/db/unit_tests/dbHierProcessorTests.cc | 9 ++++ src/gsi/unit_tests/gsiExpression.cc | 4 +- testdata/algo/deep_region_au6.gds | Bin 0 -> 9818 bytes testdata/algo/hlp12.oas | Bin 0 -> 798 bytes 13 files changed, 206 insertions(+), 37 deletions(-) create mode 100644 testdata/algo/deep_region_au6.gds create mode 100644 testdata/algo/hlp12.oas diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 1210f81b1..390301734 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -346,7 +346,7 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const { DeepLayer dl_out (m_deep_layer.derived ()); - db::BoolAndOrNotLocalOperation op (and_op); + db::BoolAndOrNotLocalOperation op (and_op, m_deep_layer.store ()->max_area_ratio (), m_deep_layer.store ()->max_vertex_count ()); db::LocalProcessor proc (const_cast (m_deep_layer.layout ()), const_cast (m_deep_layer.initial_cell ()), other->deep_layer ().layout (), other->deep_layer ().initial_cell ()); proc.set_threads (m_deep_layer.store ()->threads ()); diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 41f3909b9..2ff81f9e0 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -177,7 +177,7 @@ struct DeepShapeStore::LayoutHolder static size_t s_instance_count = 0; DeepShapeStore::DeepShapeStore () - : m_threads (1) + : m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16) { ++s_instance_count; } @@ -219,6 +219,16 @@ void DeepShapeStore::set_threads (int n) m_threads = n; } +void DeepShapeStore::set_max_area_ratio (double ar) +{ + m_max_area_ratio = ar; +} + +void DeepShapeStore::set_max_vertex_count (size_t n) +{ + m_max_vertex_count = n; +} + void DeepShapeStore::add_ref (unsigned int layout, unsigned int layer) { tl::MutexLocker locker (&m_lock); @@ -245,6 +255,13 @@ void DeepShapeStore::remove_ref (unsigned int layout, unsigned int layer) DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count) { + if (max_area_ratio == 0.0) { + max_area_ratio = m_max_area_ratio; + } + if (max_vertex_count == 0) { + max_vertex_count = m_max_vertex_count; + } + unsigned int layout_index = 0; layout_map_type::iterator l = m_layout_map.find (si); diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index c4ee8cd0c..31b18f472 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -195,7 +195,7 @@ public: * into parts satisfying the area ratio (bounding box vs. polygon area) * and maximum vertex count constraints. */ - DeepLayer create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio = 3.0, size_t max_vertex_count = 16); + DeepLayer create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio = 0.0, size_t max_vertex_count = 0); /** * @brief Inserts the deep layer's shapes into some target layout @@ -240,6 +240,40 @@ public: return m_threads; } + /** + * @brief Sets the maximum vertex count default value + * + * This parameter is used to simplify complex polygons. It is used by + * create_polygon_layer with the default parameters. It's also used by + * boolean operations when they deliver their output. + */ + void set_max_vertex_count (size_t n); + + /** + * @brief Gets the maximum vertex count + */ + size_t max_vertex_count () const + { + return m_max_vertex_count; + } + + /** + * @brief Sets the max. area ratio for bounding box vs. polygon area + * + * This parameter is used to simplify complex polygons. It is used by + * create_polygon_layer with the default parameters. It's also used by + * boolean operations when they deliver their output. + */ + void set_max_area_ratio (double ar); + + /** + * @brief Gets the max. area ratio + */ + double max_area_ratio () const + { + return m_max_area_ratio; + } + private: friend class DeepLayer; @@ -259,6 +293,8 @@ private: std::vector m_layouts; layout_map_type m_layout_map; int m_threads; + double m_max_area_ratio; + size_t m_max_vertex_count; tl::Mutex m_lock; struct DeliveryMappingCacheKey diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index d390a4ab4..6f105a063 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -101,14 +101,14 @@ public: template Ref operator() (const Ref &ref, const Trans &tr) const { - shape_type sh = ref.obj ().transformed (Trans (ref_trans_type (tr).inverted ()) * tr); + shape_type sh = ref.obj ().transformed (tr * Trans (ref.trans ())); ref_trans_type red_trans; sh.reduce (red_trans); - typename std::unordered_map >::const_iterator m = m_cache_by_shape.find (sh); + typename std::unordered_map::const_iterator m = m_cache_by_shape.find (sh); if (m != m_cache_by_shape.end ()) { - return Ref (m->second.first, ref_trans_type (tr * Trans (ref.trans ())) * m->second.second); + return Ref (m->second, red_trans); } else { @@ -118,8 +118,8 @@ public: ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (sh); } - m_cache_by_shape[sh] = std::make_pair (ptr, red_trans); - return Ref (ptr, ref_trans_type (tr * Trans (ref.trans ())) * red_trans); + m_cache_by_shape[sh] = ptr; + return Ref (ptr, red_trans); } } @@ -127,7 +127,7 @@ public: private: db::Layout *mp_layout; mutable std::unordered_map m_cache; - mutable std::unordered_map > m_cache_by_shape; + mutable std::unordered_map m_cache_by_shape; }; template @@ -494,9 +494,9 @@ private: unsigned int m_intruder_layer; db::Coord m_dist; ShapeInteractions *mp_result; - std::unordered_map, unsigned int> m_inst_shape_ids; + std::unordered_map m_inst_shape_ids; - void add_shapes_from_intruder_inst (unsigned int id1, const db::Cell &intruder_cell, const db::ICplxTrans &tn, unsigned int inst_id, const db::Box ®ion) + void add_shapes_from_intruder_inst (unsigned int id1, const db::Cell &intruder_cell, const db::ICplxTrans &tn, unsigned int /*inst_id*/, const db::Box ®ion) { db::shape_reference_translator rt (mp_subject_layout); @@ -507,18 +507,17 @@ private: si.shape_flags (polygon_ref_flags ()); while (! si.at_end ()) { - const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); + // NOTE: we intentionally rewrite to the *subject* layout - this way polygon refs in the context come from the + // subject, not from the intruder. + db::PolygonRef ref2 = rt (*si.shape ().basic_ptr (db::PolygonRef::tag ()), tn * si.trans ()); // reuse the same id for shapes from the same instance -> this avoid duplicates with different IDs on // the intruder side. - std::unordered_map, unsigned int>::const_iterator k = m_inst_shape_ids.find (std::make_pair (inst_id, ref2)); + std::unordered_map::const_iterator k = m_inst_shape_ids.find (ref2); if (k == m_inst_shape_ids.end ()) { - k = m_inst_shape_ids.insert (std::make_pair (std::make_pair (inst_id, ref2), mp_result->next_id ())).first; - - // NOTE: we intentionally rewrite to the *subject* layout - this way polygon refs in the context come from the - // subject, not from the intruder. - mp_result->add_shape (k->second, rt (*ref2, tn * si.trans ())); + k = m_inst_shape_ids.insert (std::make_pair (ref2, mp_result->next_id ())).first; + mp_result->add_shape (k->second, ref2); } @@ -887,6 +886,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, } } +// @@@ can we shortcut this if interactions is empty? { db::box_scanner2 scanner; InteractionRegistrationInst2Inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.intruder_layer (), dist, &interactions); @@ -935,6 +935,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, scanner.process (rec, dist, inst_bcs, inst_bci); } +// @@@ can we shortcut this if interactions is empty? { db::box_scanner2 scanner; InteractionRegistrationInst2Shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); @@ -1136,6 +1137,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: scanner.insert (ref, id++); } +// @@@ TODO: can we confine this search to the subject's (sized) bounding box? for (std::unordered_set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert (i.operator-> (), interactions.next_id ()); } @@ -1153,11 +1155,13 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: scanner.insert1 (ref, id++); } +// @@@ TODO: can we confine this search to the subject's (sized) bounding box? for (std::unordered_set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert2 (i.operator-> (), interactions.next_id ()); } if (intruder_shapes) { +// @@@ TODO: can we confine this search to the subject's (sized) bounding box? for (db::Shapes::shape_iterator i = intruder_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), interactions.next_id ()); } @@ -1188,6 +1192,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: // interactions low in the hierarchy. } else if (intruder_cell) { +// @@@ TODO: can we confine this search to the subject's (sized) bounding box? for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { if (! inst_bci (i->cell_inst ()).empty ()) { scanner.insert2 (&i->cell_inst (), ++inst_id); @@ -1195,6 +1200,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: } } +// @@@ TODO: can we confine this search to the subject's (sized) bounding box? for (std::unordered_set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { if (! inst_bci (*i).empty ()) { scanner.insert2 (i.operator-> (), ++inst_id); diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index c07523fb4..2f8b999e0 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -474,20 +474,10 @@ ReducingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db: reduce (shape, region, complex_region, target); } -static double area_ratio (const db::Polygon &poly) -{ - return double (poly.box ().area ()) / double (poly.area ()); -} - void ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { - size_t npoints = 0; - for (unsigned int c = 0; c < poly.holes () + 1; ++c) { - npoints += poly.contour (c).size (); - } - - if (npoints > m_max_vertex_count || area_ratio (poly) > m_area_ratio) { + if (poly.vertices () > m_max_vertex_count || poly.area_ratio () > m_area_ratio) { std::vector split_polygons; db::split_polygon (poly, split_polygons); diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index a9e5300f8..95efbb377 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -27,6 +27,7 @@ #include "dbBoxConvert.h" #include "dbEdgeProcessor.h" #include "dbPolygonGenerators.h" +#include "dbPolygonTools.h" #include "tlLog.h" #include "tlTimer.h" #include "tlInternational.h" @@ -65,12 +66,46 @@ private: std::unordered_set *mp_polyrefs; }; +class PolygonSplitter + : public PolygonSink +{ +public: + PolygonSplitter (PolygonSink &sink, double max_area_ratio, size_t max_vertex_count) + : mp_sink (&sink), m_max_area_ratio (max_area_ratio), m_max_vertex_count (max_vertex_count) + { + // .. nothing yet .. + } + + virtual void put (const db::Polygon &poly) + { + if ((m_max_vertex_count > 0 && poly.vertices () > m_max_vertex_count) || (m_max_area_ratio > 0.0 && poly.area_ratio () > m_max_area_ratio)) { + + std::vector split_polygons; + db::split_polygon (poly, split_polygons); + for (std::vector ::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) { + put (*sp); + } + + } else { + mp_sink->put (poly); + } + } + + virtual void start () { mp_sink->start (); } + virtual void flush () { mp_sink->flush (); } + +private: + PolygonSink *mp_sink; + double m_max_area_ratio; + size_t m_max_vertex_count; +}; + } // --------------------------------------------------------------------------------------------- -BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and) - : m_is_and (is_and) +BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and, double max_area_ratio, size_t max_vertex_count) + : m_is_and (is_and), m_max_area_ratio (max_area_ratio), m_max_vertex_count (max_vertex_count) { // .. nothing yet .. } @@ -133,7 +168,8 @@ BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const ShapeIntera db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); db::PolygonRefGenerator pr (layout, result); - db::PolygonGenerator pg (pr, true, true); + db::PolygonSplitter splitter (pr, m_max_area_ratio, m_max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); ep.process (pg, op); } diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h index 8fe9ae0be..c80086fdc 100644 --- a/src/db/db/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -114,7 +114,7 @@ class DB_PUBLIC BoolAndOrNotLocalOperation : public LocalOperation { public: - BoolAndOrNotLocalOperation (bool is_and); + BoolAndOrNotLocalOperation (bool is_and, double max_area_ratio = 0.0, size_t max_vertex_count = 0); virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const; virtual on_empty_intruder_mode on_empty_intruder_hint () const; @@ -122,6 +122,8 @@ public: private: bool m_is_and; + double m_max_area_ratio; + size_t m_max_vertex_count; }; /** diff --git a/src/db/db/dbPolygon.h b/src/db/db/dbPolygon.h index 09a09832e..94941f6e7 100644 --- a/src/db/db/dbPolygon.h +++ b/src/db/db/dbPolygon.h @@ -1662,7 +1662,7 @@ public: } /** - * @brief Return the number of points in the polygon + * @brief Returns the number of points in the polygon */ size_t vertices () const { @@ -1673,6 +1673,18 @@ public: return n; } + /** + * @brief Returns the area ratio between the polygon's bounding box and actual area + * + * This number is a measure how well the polygon is approximated by the bounding box. + * Values are bigger than 1 for well-formed polygons. Bigger values mean worse + * approximation. + */ + double area_ratio () const + { + return double (box ().area ()) / double (area ()); + } + /** * @brief The hull "begin" point iterator * @@ -2956,6 +2968,18 @@ public: return m_hull.size (); } + /** + * @brief Returns the area ratio between the polygon's bounding box and actual area + * + * This number is a measure how well the polygon is approximated by the bounding box. + * Values are bigger than 1 for well-formed polygons. Bigger values mean worse + * approximation. + */ + double area_ratio () const + { + return double (box ().area ()) / double (area ()); + } + void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const { db::mem_stat (stat, purpose, cat, m_hull, no_self, parent); diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 41b148e58..863566678 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -288,3 +288,54 @@ TEST(5_BoolXOR) db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au5.gds"); } +TEST(6_Reduction) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + unsigned int lbox = ly.insert_layer (); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42), dss); + + top_cell.shapes (lbox).insert (db::Box (2000, -1000, 6000, 4000)); + db::Region box (db::RecursiveShapeIterator (ly, top_cell, lbox), dss); + + db::Region r2xor3 = r2 ^ r3; + db::Region r2xorbox = r2 ^ box; + db::Region r2xor42 = r2 ^ r42; + db::Region rboxxor3 = box ^ r3; + db::Region r42xor3 = r42 ^ r3; + db::Region r42xor42 = r42 ^ r42; + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2xor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2xorbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2xor42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rboxxor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r42xor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r42xor42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au6.gds"); +} + diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 3453aacd9..53870f177 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -1061,3 +1061,12 @@ TEST(BasicSelfOverlapWithSize10) run_test_bool_with_size (_this, "hlp10.oas", TMSelfOverlap, 150, 111); } +TEST(TopWithBelow1) +{ + run_test_bool (_this, "hlp12.oas", TMNot, 100); +} + +TEST(TopWithBelow2) +{ + run_test_bool (_this, "hlp12.oas", TMNotSwapped, 101); +} diff --git a/src/gsi/unit_tests/gsiExpression.cc b/src/gsi/unit_tests/gsiExpression.cc index 960c30c1a..500ee0da4 100644 --- a/src/gsi/unit_tests/gsiExpression.cc +++ b/src/gsi/unit_tests/gsiExpression.cc @@ -235,7 +235,7 @@ TEST(2) v = e.parse ("var b=B.new; b.bx(-1)").execute (); EXPECT_EQ (v.to_string (), std::string ("xz")); /* - @@@ No detailed type analysis for ambiguity resolution so far: + TODO: No detailed type analysis for ambiguity resolution so far: v = e.parse ("var b=B.new; b.bx('hello', 1)").execute (); EXPECT_EQ (v.to_string (), std::string ("20.5")); */ @@ -297,10 +297,8 @@ TEST(2) v = e.parse ("b.amember_ref.a5(177)").execute (); EXPECT_EQ (v.to_string (), std::string ("nil")); - // @@@ v = e.parse ("b.amember_or_nil(true)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: 177")); - // @@@ bool error = false; try { diff --git a/testdata/algo/deep_region_au6.gds b/testdata/algo/deep_region_au6.gds new file mode 100644 index 0000000000000000000000000000000000000000..6609f6f4071cc0c2333e0ae7ac461b2abbb344db GIT binary patch literal 9818 zcmbW7U5H&*8HU&V^vq=b9Fxo>3==yV)5ht9+B9v5os5YI6DN)1Br}ANWV9gkq9_C^ zy^t0Pk%Gm55b?qv2=qdb7KB0(saKU;M1?{n7g`}$=|wRR2o%)B<9hbHUe`G>YsK~^ zPx!w5?(cW6v(HIFEA;lB3e!Ek1Ah&}VKVfDEn!#Z-@{;U>)6tlLfG7Q-@?J~?S1m{ z&u{EI{HupA{p9pon7*&98)~)IP8?ib4WWN9gw|+p7z(Yi-VnmvcwhYUg?9VjJ75Ss ze-5GdWz+VC{hz$6zTLk49Dazd-EN;ZZLjO|^&#}lC=WB=FBIdq&$rvRt`WD1(@{^h z-@4YNAIbIKng5;j+ux-h>(uA?cjo`U@drY6QiHA5((=jOp?@fZp^cu}$ar6@>|E?j zoB&g}yLafuUA(QSo)<%icZf6jLE#Q}T;u!PZC83WX*b~DmDsD%Om-*vi?h0YQ@a5N zuf(dZXEJN~yL#QdtMA4h?}>Fp_Wv}-Kb&cMWHJ6f&c+8|t=)zLZ>p=N?X9&geb=mS z-;8_KQl8!U+YU>5`qTKEK6Urc_PUq=rT%;DYCKg=EG-}Ts`hbkqc*qM zv}VtuJ?(0jZ)l}!3%h)qFO_|tx>5Fh?m*f1X!q84#oK?ato!ok%f1sYmVIa6&c5^n z4d>G$gl!*&uwgldtea&^$jb609UjO|2iVul@F!s+Re)1RNPhC|U z_Jp|Xk7#>6{=UnK4~e_*r%d7}e=+`iGk@htT=EwcKlzLLeV26|anKQ8w7s5x>v81| z$$ua|uO|5?e=+{yNyRxlsklqCrtS6kM;_O8M`C>PK6cc!y{>O1J$a%he^L76FXsR7 zbE+Rw_nwzCsXzIP`crR657Dn*$wZ(01-;H4&WUzQI?f;N18tA@Dd@>hb&@A_lfP(t zy?e_cnO=*VBRy{_N! zfUe`5L&y0i+FsY6dPCPSXLQV8w7srBb5+-!xhmaLADFh^ORxT;r|;-Z`j5TV|K+C@ zXZdNxUHy(}dp$ln;y~h}H*K%QKmL;99Dhl1PyX1ny&iwthq{jU2>QFS^`Y}TVGhY()DxFJLFy&GY5N`h8|{35#BXX%{i5P0e^F1~d~<@tb6jY9WKqBIVvG~_uVKt%;(5hwXnXhmb@hK_ zUHXxA^^JbhztLN^H!Q~AbU}GS@}GD;ll+svsNb+(eS_$S=Q8PI@)z}7Pn`eizLE5i zw%7XSKDlq+n})G}$zP0b-Nf_i8}-w_iPt;kaIHMQ$-n7(NWPvsV~_r#e$xfz={ZsddAP3RFY1Tqq^JMnI~@Bi+TO6PkA3!inthCW?7eL4mHqXe zQZM_$b1cTV@xIAlj6d>2`{AGQ7Zn&Q3hdH?Kz7O{3FX}r#KRB=4hg5f* zUsFd>PaMu!$L&1NY_FZa8}@5Xeh!%<{c|44UyMKfp7hi2#Xio&-x2;|+TO6JpXt-? zLi#szJCmJG{)%4j?p((&Sm;ID8y58Zaz=jo1+8x;eimORe`tF>|BvwtSjYLRU;g|{ zpMUu?ZIAOPSBSKZKQM)u)wb)wko{GHtK*F9i0U zJ%#Kq`)}G_>)#XaNDtA?{~;57@)!HZFSwi={L;dj8)Nk5e%YXHv z;;dd&+_ej)?e+M~kNlY*c{4xJ_PU<=@!g&LdG5(yw7ow6C*RfnLH2LuMkf20{Kft+ z+*F!wFUH^V&3H|G{vglaV}H!Fz4rWZZV(ePcg_#f z_S*bU&MALL-e(SEl7I3S^IzPq_>j0qwq+7O`HTAX`1!;9*W>fY+`kr|KhgI3{MlRL zK;pB%rtP)*SI?^dkp8dzHk1A*e=+~lZ)s0Xzoor7^BdFl`u=>3f5GYa{88Vilm1aR z{WEQ^)qnr*RX?QuPsf@~>QDY+{hS}nkNz_^`Y+mEpWhSj=sM0xbex}}?REXuW7=QH z{%(6Jll@KpV*cx|s(wh_U*DWb{mEa{Gk5w7={xf`ZLjrz>5%jg-O>G-=##$~e{s9| z#B)a9c>YA&>-}SYnIHM1BY)BMx}N8k^Pm0W+-Ltp+w1#3_jkcsGIqTw%6-te)Ny|p<{lc?REW3pRVKm z86EG>qV07(pMT7s&p+nQ=bvbMeg1prH9yGw=2tSAU-B3GzYsOd2{OL}pUt#AvaFAJ zGbi$AZsc#;UYj4^f2g1LKkDZFPqe*WKj#qZf9A*jP(OP^{i5yl h{ULASK;n|WX?ra`^%EaGanXym=lJn|aP;?w{{YKcJ{JH0 literal 0 HcmV?d00001 diff --git a/testdata/algo/hlp12.oas b/testdata/algo/hlp12.oas new file mode 100644 index 0000000000000000000000000000000000000000..259bf940c8883554a5067d2aff61e96c3b5b7c9d GIT binary patch literal 798 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>um*Yhx%)G-h6FkK1v9gF`h^)WL&SI) zM6H<^7?~O*zcBx1Jjlb#DENY5AsaWt2T>ux%mr*v=?knESa&eL;1RW9Wa?1=!aSSf zD>FC40yY6r3r403tSdxLO0AIOWfDBW$jxyx4Ux>O8)uFypL{u?_(d2~B zKDL*t`4?I9RsyYDA(?l9HUEX`8;O;Y>xC||t`mBx`i1#{%NY*BYBmRS@Lh=N Date: Mon, 26 Nov 2018 21:29:09 +0100 Subject: [PATCH 064/335] Fixed a small build issue (ambiguous parameters) --- src/db/unit_tests/dbHierarchyBuilderTests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc index 84a7394f5..14798df78 100644 --- a/src/db/unit_tests/dbHierarchyBuilderTests.cc +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -71,7 +71,7 @@ TEST(2_WithoutClip) } db::Layout target; - db::HierarchyBuilder builder (&target, false); + db::HierarchyBuilder builder (&target); db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); From 6e52c37a1ebdbaba58a0dbdd53097ad14778402a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 26 Nov 2018 22:28:29 +0100 Subject: [PATCH 065/335] WIP: Fixed another build issue (ambiguous overload) --- src/db/unit_tests/dbHierarchyBuilderTests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc index 14798df78..55e0d6e76 100644 --- a/src/db/unit_tests/dbHierarchyBuilderTests.cc +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -40,7 +40,7 @@ TEST(1) } db::Layout target; - db::HierarchyBuilder builder (&target, false); + db::HierarchyBuilder builder (&target); for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { From 970f22960fd0263288ec557e722bb03afdcf87b0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 28 Nov 2018 00:00:06 +0100 Subject: [PATCH 066/335] WIP: enabled tests in editable mode too. --- src/db/unit_tests/dbHierProcessorTests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 53870f177..6389d8ff0 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -129,7 +129,7 @@ private: void normalize_layer (db::Layout &layout, unsigned int layer) { for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { - db::Shapes s; + db::Shapes s (layout.is_editable ()); s.swap (c->shapes (layer)); for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); !i.at_end (); ++i) { db::Polygon poly; From 3609bdda8121556f976c35f75ccac74f75712b1f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Dec 2018 09:38:16 +0100 Subject: [PATCH 067/335] WIP: first steps for network processor. --- src/db/db/db.pro | 3 +- src/db/db/dbHierNetworkProcessor.cc | 217 ++++++++++++++++++ src/db/db/dbHierNetworkProcessor.h | 97 ++++++++ .../unit_tests/dbHierNetworkProcessorTests.cc | 117 ++++++++++ src/db/unit_tests/unit_tests.pro | 3 +- 5 files changed, 435 insertions(+), 2 deletions(-) create mode 100644 src/db/db/dbHierNetworkProcessor.cc create mode 100644 src/db/db/dbHierNetworkProcessor.h create mode 100644 src/db/unit_tests/dbHierNetworkProcessorTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 7d2d2d563..3ac6db9e4 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -140,7 +140,8 @@ SOURCES = \ dbHierarchyBuilder.cc \ dbLocalOperation.cc \ dbHierProcessor.cc \ - dbDeepRegion.cc + dbDeepRegion.cc \ + dbHierNetworkProcessor.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc new file mode 100644 index 000000000..414d248a5 --- /dev/null +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -0,0 +1,217 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbHierNetworkProcessor.h" +#include "dbShape.h" +#include "dbShapes.h" +#include "dbInstElement.h" +#include "dbPolygon.h" +#include "dbPolygonTools.h" +#include "dbBoxConvert.h" + +#include +#include + +namespace db +{ + +// ------------------------------------------------------------------------------ +// Connectivity implementation + +Connectivity::Connectivity () +{ + // .. nothing yet .. +} + +void +Connectivity::connect (unsigned int la, unsigned int lb) +{ + m_connected [la].insert (lb); + m_connected [lb].insert (la); + m_all_layers.insert (la); + m_all_layers.insert (lb); +} + +void +Connectivity::connect (unsigned int l) +{ + m_connected [l].insert (l); + m_all_layers.insert (l); +} + +Connectivity::layer_iterator +Connectivity::begin_layers () +{ + return m_all_layers.begin (); +} + +Connectivity::layer_iterator +Connectivity::end_layers () +{ + return m_all_layers.end (); +} + +Connectivity::layers_type s_empty_layers; + +Connectivity::layer_iterator +Connectivity::begin_connected (unsigned int layer) +{ + std::map::const_iterator i = m_connected.find (layer); + if (i == m_connected.end ()) { + return s_empty_layers.begin (); + } else { + return i->second.begin (); + } +} + +Connectivity::layer_iterator +Connectivity::end_connected (unsigned int layer) +{ + std::map::const_iterator i = m_connected.find (layer); + if (i == m_connected.end ()) { + return s_empty_layers.end (); + } else { + return i->second.end (); + } +} + +static bool +interaction_test (const db::PolygonRef &a, const db::PolygonRef &b) +{ + // TODO: this could be part of db::interact (including transformation) + if (a.obj ().is_box () && b.obj ().is_box ()) { + return db::interact (a.obj ().box ().transformed (b.trans ().inverted () * a.trans ()), b.obj ().box ()); + } else { + return db::interact (a.obj ().transformed (b.trans ().inverted () * a.trans ()), b.obj ()); + } +} + +template +bool Connectivity::interacts (const T &a, unsigned int la, T &b, unsigned int lb) const +{ + std::map::const_iterator i = m_connected.find (la); + if (i == m_connected.end () || i->second.find (lb) == i->second.end ()) { + return false; + } else { + return interaction_test (a, b); + } +} + +// explicit instantiations +template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, db::PolygonRef &b, unsigned int lb) const; + +// ------------------------------------------------------------------------------ +// ... + + +#if 0 +/** + * @brief Represents a cluster of shapes + * + * A cluster of shapes is a set of shapes which are connected in terms + * of a given connectivity. + */ +class LocalCluster +{ +public: + typedef size_t id_type; + + LocalCluster (); + + void connect (const db::Shape &a, unsigned int la, const db::Shape &b, unsigned int lb); + id_type id () const; + + // @@@ Trans is the transformation of the cluster to the shape (instance transformation) + void interacts (const db::Shape &s, unsigned int ls, const db::ICplxTrans &trans, const Connectivity &conn); + const db::Box &bbox () const; + +private: + friend class LocalClusters; + + void set_id (id_type id); +}; + +/** + * @brief Represents a collection of clusters in a cell + * + * After all shapes in a cell are connected, the cluster collection is filled if + * disconnected clusters. + */ +class LocalClusters +{ +public: + LocalClusters (); + + // @@@ needs to be fast(!) + LocalCluster::id_type cluster_id_for_shape (const db::Shape &s, unsigned int ls) const; + const LocalCluster &cluster (LocalCluster::id_type id) const; + LocalCluster &create_cluster (); + void remove_cluster (LocalCluster::id_type id); + cluster_iterator find (const db::Box ®ion); + + // @@@ Trans is the transformation of the clusters to the shape (instance transformation) + std::vector interacting_clusters (const db::Shape &s, unsigned int ls, const db::ICplxTrans &trans, const Connectivity &conn) const; + // @@@ Trans is the transformation of the clusters to the clusters looked up (instance transformation) + std::vector interacting_clusters (const LocalClusters &c, const db::ICplxTrans &trans, const Connectivity &conn) const; +}; + +/** + * @brief Represents all clusters in a cell and their connections to child cells + * + * Connections to indirect children are made through empty dummy clusters. + * Also, connections between two clusters of different children without + * mediating cluster on cell level are made through empty dummy clusters. + * Hence, there is always a cluster in the parent cell which connects to + * clusters from child cells. + */ +class HierarchicalClusters +{ +public: + HierarchicalClusters (); + + LocalClusters &local (); + + // build local clusters + // determine local to cell cluster interactions -> top-down connection, maybe across dummy + // identify cell overlaps + // determine cell-to-cell cluster interactions -> make dummy cluster to connect both, connect to each child + // maybe across dummy cluster + // shall be called bottom-up + void build_clusters (const db::Cell &cell, const Connectivity &conn); + + // @@@ trans is the transformation from child to this (instance transformation) + // used by child clusters to verify whether they are connected somewhere in the parent + LocalCluster::id_type is_connected_to (LocalCluster::id_type id, const db::InstElement &trans) const; + + // connections to subcells (== pins?) + const std::vector > &top_down_connections (LocalCluster::id_type) const; + + // propagate all connected clusters to their parents + // -> creates new local clusters, removes them from their local set + // used by the merge step to form local-only clusters + // can be called bottom-up + void propagate_connected (); +}; +#endif + +} diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h new file mode 100644 index 000000000..de1605328 --- /dev/null +++ b/src/db/db/dbHierNetworkProcessor.h @@ -0,0 +1,97 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbHierNetworkProcessor +#define HDR_dbHierNetworkProcessor + +#include "dbCommon.h" + +#include +#include + +namespace db { + +/** + * @brief Defines the connectivity + * + * Connectivity is defined in terms of layers. Certain layer pairs + * are connected when shapes on their layers interact. + * Connectivity includes intra-layer connectivity - i.e. + * shapes on a layer are not connected by default. They need to + * be connected explicitly using "connect(layer)". + */ +class DB_PUBLIC Connectivity +{ +public: + typedef std::set layers_type; + typedef layers_type::const_iterator layer_iterator; + + /** + * @brief Creates a connectivity object without any connections + */ + Connectivity (); + + /** + * @brief Adds inter-layer connectivity + */ + void connect (unsigned int la, unsigned int lb); + + /** + * @brief Adds intra-layer connectivity for layer l + */ + void connect (unsigned int l); + + /** + * @brief Begin iterator for the layers involved + */ + layer_iterator begin_layers (); + + /** + * @brief End iterator for the layers involved + */ + layer_iterator end_layers (); + + /** + * @brief Begin iterator for the layers connected to a specific layer + */ + layer_iterator begin_connected (unsigned int layer); + + /** + * @brief End iterator for the layers connected to a specific layer + */ + layer_iterator end_connected (unsigned int layer); + + /** + * @brief Returns true, if the given shapes on the given layers interact + */ + template + bool interacts (const T &a, unsigned int la, T &b, unsigned int lb) const; + +private: + layers_type m_all_layers; + std::map m_connected; +}; + +} + +#endif diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc new file mode 100644 index 000000000..c049cfd0b --- /dev/null +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -0,0 +1,117 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbHierNetworkProcessor.h" +#include "dbTestSupport.h" +#include "dbShapeRepository.h" +#include "dbPolygon.h" +#include "dbPath.h" +#include "dbText.h" + +static std::string l2s (db::Connectivity::layer_iterator b, db::Connectivity::layer_iterator e) +{ + std::string s; + for (db::Connectivity::layer_iterator i = b; i != e; ++i) { + if (! s.empty ()) { + s += ","; + } + s += tl::to_string (*i); + } + return s; +} + +TEST(1_Connectivity) +{ + db::Connectivity conn; + + EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), ""); + + conn.connect (0); + EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), "0"); + EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0"); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), ""); + + conn.connect (0, 1); + EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), "0,1"); + EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1"); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0"); + + conn.connect (1); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0,1"); + + conn.connect (0, 2); + conn.connect (2); + + EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1,2"); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0,1"); + EXPECT_EQ (l2s (conn.begin_connected (2), conn.end_connected (2)), "0,2"); +} + +TEST(2_ShapeInteractions) +{ + db::Connectivity conn; + + conn.connect (0); + conn.connect (1); + conn.connect (0, 1); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + db::GenericRepository repo; + db::PolygonRef ref1 (poly, repo); + db::PolygonRef ref2 (poly.transformed (db::Trans (db::Vector (0, 10))), repo); + db::PolygonRef ref3 (poly.transformed (db::Trans (db::Vector (0, 2000))), repo); + + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); +} + +TEST(2_ShapeInteractionsRealPolygon) +{ + db::Connectivity conn; + + conn.connect (0); + conn.connect (1); + conn.connect (0, 1); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;500,1000;500,1500;1000,1500;1000,0)", poly); + db::GenericRepository repo; + db::PolygonRef ref1 (poly, repo); + db::PolygonRef ref2 (poly.transformed (db::Trans (db::Vector (0, 10))), repo); + db::PolygonRef ref3 (poly.transformed (db::Trans (db::Vector (0, 2000))), repo); + db::PolygonRef ref4 (poly.transformed (db::Trans (db::Vector (0, 1500))), repo); + + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref4, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index b35a4ef9e..052a65b78 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -57,7 +57,8 @@ SOURCES = \ dbRecursiveShapeIteratorTests.cc \ dbHierProcessorTests.cc \ dbDeepRegionTests.cc \ - dbDeepShapeStoreTests.cc + dbDeepShapeStoreTests.cc \ + dbHierNetworkProcessorTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC From 59d1aead598571a041d39982bb0b0e465b99bf64 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Dec 2018 01:30:56 +0100 Subject: [PATCH 068/335] WIP: new classes for hier network processor. --- src/db/db/dbBoxScanner.h | 44 +++- src/db/db/dbHierNetworkProcessor.cc | 201 +++++++++++++++--- src/db/db/dbHierNetworkProcessor.h | 100 ++++++++- src/db/unit_tests/dbBoxScanner.cc | 65 +++++- .../unit_tests/dbHierNetworkProcessorTests.cc | 28 ++- 5 files changed, 393 insertions(+), 45 deletions(-) diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index 4d6ee0f70..5c9372fe8 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -129,6 +129,14 @@ struct box_scanner_receiver * definition. */ void add (const Obj * /*o1*/, const Prop & /*p1*/, const Obj * /*o2*/, const Prop & /*p2*/) { } + + /** + * @brief Indicates whether the scanner may stop + * + * The scanner will stop if this method returns true. This feature can be used to + * terminate the scan process early if the outcome is known. + */ + bool stop () const { return false; } }; /** @@ -250,9 +258,13 @@ public: * * The box converter must be capable of converting the Obj object into a box. * It must provide a box_type typedef. + * + * The scanner process can be terminated early by making the receiver's + * stop() method return true. In this case, this method will return false. + * Otherwise it will return true. */ template - void process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ()) + bool process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ()) { typedef typename BoxConvert::box_type box_type; typedef typename box_type::coord_type coord_type; @@ -269,6 +281,9 @@ public: for (iterator_type j = i + 1; j != m_pp.end (); ++j) { if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) { rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } } } } @@ -355,6 +370,9 @@ public: if (seen.insert (std::make_pair (i->first, j->first)).second) { seen.insert (std::make_pair (j->first, i->first)); rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } } } } @@ -379,6 +397,8 @@ public: } + return true; + } private: @@ -421,6 +441,14 @@ struct box_scanner_receiver2 * definition. */ void add (const Obj1 * /*o1*/, const Prop1 & /*p1*/, const Obj2 * /*o2*/, const Prop2 & /*p2*/) { } + + /** + * @brief Indicates whether the scanner may stop + * + * The scanner will stop if this method returns true. This feature can be used to + * terminate the scan process early if the outcome is known. + */ + bool stop () const { return false; } }; /** @@ -558,9 +586,13 @@ public: * The box converter 1 must be capable of converting the Obj1 object into a box. * It must provide a box_type typedef. The box converter 2 must be able to convert Obj2 to * a box. The box type of both box converters must be identical. + * + * The scanner process can be terminated early by making the receiver's + * stop() method return true. In this case, this method will return false. + * Otherwise it will return true. */ template - void process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ()) + bool process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ()) { typedef typename BoxConvert1::box_type box_type; // must be same as BoxConvert2::box_type typedef typename box_type::coord_type coord_type; @@ -592,6 +624,9 @@ public: for (iterator_type2 j = m_pp2.begin (); j != m_pp2.end (); ++j) { if (bs_boxes_overlap (bc1 (*i->first), bc2 (*j->first), enl)) { rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } } } } @@ -716,6 +751,9 @@ public: if (seen1.insert (std::make_pair (i->first, j->first)).second) { seen2.insert (std::make_pair (j->first, i->first)); rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } } } } @@ -747,6 +785,8 @@ public: } + return true; + } private: diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 414d248a5..bd48e5800 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -27,7 +27,7 @@ #include "dbInstElement.h" #include "dbPolygon.h" #include "dbPolygonTools.h" -#include "dbBoxConvert.h" +#include "dbBoxScanner.h" #include #include @@ -95,62 +95,193 @@ Connectivity::end_connected (unsigned int layer) } } +template static bool -interaction_test (const db::PolygonRef &a, const db::PolygonRef &b) +interaction_test (const db::PolygonRef &a, const db::PolygonRef &b, const Trans &trans) { // TODO: this could be part of db::interact (including transformation) if (a.obj ().is_box () && b.obj ().is_box ()) { - return db::interact (a.obj ().box ().transformed (b.trans ().inverted () * a.trans ()), b.obj ().box ()); + return db::interact (a.obj ().box ().transformed (b.trans ().inverted () * a.trans ()), b.obj ().box ().transformed (trans)); } else { - return db::interact (a.obj ().transformed (b.trans ().inverted () * a.trans ()), b.obj ()); + return db::interact (a.obj ().transformed (b.trans ().inverted () * a.trans ()), b.obj ().transformed (trans)); } } -template -bool Connectivity::interacts (const T &a, unsigned int la, T &b, unsigned int lb) const +template +bool Connectivity::interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans) const { std::map::const_iterator i = m_connected.find (la); if (i == m_connected.end () || i->second.find (lb) == i->second.end ()) { return false; } else { - return interaction_test (a, b); + return interaction_test (a, b, trans); } } // explicit instantiations -template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, db::PolygonRef &b, unsigned int lb) const; +template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::UnitTrans &trans) const; +template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::ICplxTrans &trans) const; // ------------------------------------------------------------------------------ -// ... +// local_cluster implementation + +template +local_cluster::local_cluster () + : m_id (0), m_needs_update (false) +{ + // .. nothing yet .. +} + +template +void +local_cluster::add (const T &s, unsigned int la) +{ + m_shapes[la].insert (s); + m_needs_update = true; +} + +template +void +local_cluster::join_with (const local_cluster &other) +{ + for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { + tree_type &other_tree = m_shapes[s->first]; + other_tree.insert (s->second.begin (), s->second.end ()); + } + + m_needs_update = true; +} + +template +void +local_cluster::ensure_sorted () +{ + if (! m_needs_update) { + return; + } + + // sort the shape trees + for (typename std::map::iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + s->second.sort (db::box_convert ()); + } + + // recompute bounding box + m_bbox = box_type (); + db::box_convert bc; + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + for (typename tree_type::const_iterator i = s->second.begin (); i != s->second.end (); ++i) { + m_bbox += bc (*i); + } + } + + m_needs_update = false; +} + +namespace +{ + +template +struct interaction_receiver + : public box_scanner_receiver2 +{ +public: + typedef typename local_cluster::box_type box_type; + + interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans) + : mp_conn (&conn), m_any (false), m_trans (trans) + { + // .. nothing yet .. + } + + void add (const T *s1, unsigned int l1, const T *s2, unsigned int l2) + { + if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans)) { + m_any = true; + } + } + + bool stop () const + { + return m_any; + } + +private: + const Connectivity *mp_conn; + bool m_any; + db::ICplxTrans m_trans; +}; + +template +struct transformed_box +{ + typedef db::box_convert base_bc; + typedef typename T::box_type box_type; + + transformed_box (const Trans &trans) + : m_trans (trans) + { + // .. nothing yet .. + } + + box_type operator() (const T &t) const + { + return m_bc (t).transformed (m_trans); + } + +private: + base_bc m_bc; + Trans m_trans; +}; + +} + +template +bool +local_cluster::interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const +{ + const_cast *> (this)->ensure_sorted (); + + if (! other.bbox ().overlaps (bbox ())) { + return false; + } + + box_type common = other.bbox () & bbox (); + + db::box_scanner2 scanner; + transformed_box bc_t (trans); + db::box_convert bc; + + bool any = false; + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + for (typename tree_type::overlapping_iterator i = s->second.begin_overlapping (common, bc); ! i.at_end (); ++i) { + scanner.insert1 (i.operator-> (), s->first); + any = true; + } + } + + if (! any) { + return false; + } + + for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { + for (typename tree_type::overlapping_iterator i = s->second.begin_overlapping (common.transformed (trans.inverted ()), bc); ! i.at_end (); ++i) { + scanner.insert2 (i.operator-> (), s->first); + } + } + + interaction_receiver rec (conn, trans); + return ! scanner.process (rec, 0, bc, bc_t); +} + +// explicit instantiations +template class DB_PUBLIC local_cluster; + + +// ------------------------------------------------------------------------------ +// local_cluster implementation #if 0 -/** - * @brief Represents a cluster of shapes - * - * A cluster of shapes is a set of shapes which are connected in terms - * of a given connectivity. - */ -class LocalCluster -{ -public: - typedef size_t id_type; - - LocalCluster (); - - void connect (const db::Shape &a, unsigned int la, const db::Shape &b, unsigned int lb); - id_type id () const; - - // @@@ Trans is the transformation of the cluster to the shape (instance transformation) - void interacts (const db::Shape &s, unsigned int ls, const db::ICplxTrans &trans, const Connectivity &conn); - const db::Box &bbox () const; - -private: - friend class LocalClusters; - - void set_id (id_type id); -}; - /** * @brief Represents a collection of clusters in a cell * diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index de1605328..80e53a1cd 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -25,6 +25,9 @@ #define HDR_dbHierNetworkProcessor #include "dbCommon.h" +#include "dbTrans.h" +#include "dbBoxConvert.h" +#include "dbBoxTree.h" #include #include @@ -81,17 +84,112 @@ public: */ layer_iterator end_connected (unsigned int layer); + /** + * @brief Returns true, if the given shapes on the given layers interact + * + * This method accepts a transformation. This transformation is applied + * to the b shape before checking against a. + */ + template + bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans) const; + /** * @brief Returns true, if the given shapes on the given layers interact */ template - bool interacts (const T &a, unsigned int la, T &b, unsigned int lb) const; + bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb) const + { + return interacts (a, la, b, lb, UnitTrans ()); + } private: layers_type m_all_layers; std::map m_connected; }; +/** + * @brief Represents a cluster of shapes + * + * A cluster of shapes is a set of shapes of type T which are connected in terms + * of a given connectivity. The shapes will still be organised in layers. + */ +template +class DB_PUBLIC local_cluster +{ +public: + typedef size_t id_type; + typedef typename T::box_type box_type; + + /** + * @brief Creates an empty cluster + */ + local_cluster (); + + /** + * @brief Adds a shape with the given layer to the cluster + */ + void add (const T &s, unsigned int la); + + /** + * @brief Joins this cluster with the other one + * + * This will copy all shapes from the other cluster into ourself. + */ + void join_with (const local_cluster &other); + + /** + * @brief Gets the cluster's ID + * + * The ID is a unique identifier for the cluster. An ID value of 0 is reserved for + * "no cluster". + */ + id_type id () const + { + return m_id; + } + + /** + * @brief Tests whether this cluster interacts with another cluster + * + * "trans" is the transformation which is applied to the other cluster before + * the test. + */ + bool interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const; + + /** + * @brief Gets the bounding box of this cluster + */ + const box_type &bbox () const + { + const_cast *> (this)->ensure_sorted (); // also updates bbox + return m_bbox; + } + +private: + template friend class local_clusters; + template friend class interaction_receiver; + typedef db::unstable_box_tree > tree_type; + + void set_id (id_type id) + { + m_id = id; + } + + const T &shape (unsigned int l, size_t index) const + { + typename std::map::const_iterator s = m_shapes.find (l); + tl_assert (s != m_shapes.end ()); + return s->second.objects () [index]; + } + + void ensure_sorted (); + + id_type m_id; + bool m_needs_update; + std::map m_shapes; + box_type m_bbox; +}; + } #endif diff --git a/src/db/unit_tests/dbBoxScanner.cc b/src/db/unit_tests/dbBoxScanner.cc index 211b5a4fa..5e3e1e591 100644 --- a/src/db/unit_tests/dbBoxScanner.cc +++ b/src/db/unit_tests/dbBoxScanner.cc @@ -35,6 +35,8 @@ struct BoxScannerTestRecorder str += "<" + tl::to_string (p) + ">"; } + bool stop () const { return false; } + void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; @@ -43,10 +45,32 @@ struct BoxScannerTestRecorder std::string str; }; +struct BoxScannerTestRecorderStopping +{ + BoxScannerTestRecorderStopping () : do_stop (false) { } + + void finish (const db::Box * /*box*/, size_t p) { + str += "<" + tl::to_string (p) + ">"; + } + + bool stop () const { return do_stop; } + + void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) + { + str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; + do_stop = true; + } + + std::string str; + bool do_stop; +}; + struct BoxScannerTestRecorder2 { void finish (const db::Box *, size_t) { } + bool stop () const { return false; } + void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { interactions.insert (std::make_pair (p1, p2)); @@ -66,6 +90,8 @@ struct BoxScannerTestRecorderTwo str += "<" + tl::to_string (p) + ">"; } + bool stop () const { return false; } + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) { str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; @@ -74,11 +100,37 @@ struct BoxScannerTestRecorderTwo std::string str; }; +struct BoxScannerTestRecorderTwoStopping +{ + BoxScannerTestRecorderTwoStopping () : do_stop (false) { } + + void finish1 (const db::Box * /*box*/, size_t p) { + str += "<" + tl::to_string (p) + ">"; + } + + void finish2 (const db::SimplePolygon * /*poly*/, int p) { + str += "<" + tl::to_string (p) + ">"; + } + + bool stop () const { return do_stop; } + + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) + { + str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; + do_stop = true; + } + + std::string str; + bool do_stop; +}; + struct BoxScannerTestRecorder2Two { void finish1 (const db::Box *, size_t) { } void finish2 (const db::SimplePolygon *, int) { } + bool stop () const { return false; } + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) { interactions.insert (std::make_pair (p1, p2)); @@ -106,8 +158,12 @@ TEST(1) bs.set_fill_factor (0.0); db::box_convert bc; bs.set_scanner_threshold (0); - bs.process (tr, 1, bc); + EXPECT_EQ (bs.process (tr, 1, bc), true); EXPECT_EQ (tr.str, "(4-2)(5-2)(5-4)(3-2)(3-4)(5-3)<2><5><4><3>(1-0)<0><1>"); + + BoxScannerTestRecorderStopping trstop; + EXPECT_EQ (bs.process (trstop, 1, bc), false); + EXPECT_EQ (trstop.str, "(4-2)"); } TEST(1a) @@ -920,8 +976,13 @@ TEST(two_1b) db::box_convert bc1; db::box_convert bc2; bs.set_scanner_threshold (0); - bs.process (tr, 1, bc1, bc2); + EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); EXPECT_EQ (tr.str, "(1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>"); + + + BoxScannerTestRecorderTwoStopping trstop; + EXPECT_EQ (bs.process (trstop, 1, bc1, bc2), false); + EXPECT_EQ (trstop.str, "(1-12)"); } void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index c049cfd0b..23176b8fd 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -80,15 +80,23 @@ TEST(2_ShapeInteractions) tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); db::GenericRepository repo; db::PolygonRef ref1 (poly, repo); - db::PolygonRef ref2 (poly.transformed (db::Trans (db::Vector (0, 10))), repo); - db::PolygonRef ref3 (poly.transformed (db::Trans (db::Vector (0, 2000))), repo); + db::ICplxTrans t2 (db::Trans (db::Vector (0, 10))); + db::PolygonRef ref2 (poly.transformed (t2), repo); + db::ICplxTrans t3 (db::Trans (db::Vector (0, 2000))); + db::PolygonRef ref3 (poly.transformed (t3), repo); EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2), true); // t2*ref1 == ref2 EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2), true); EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2), true); EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3), false); // t3*ref1 == ref3 EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3), false); EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false); } TEST(2_ShapeInteractionsRealPolygon) @@ -103,15 +111,25 @@ TEST(2_ShapeInteractionsRealPolygon) tl::from_string ("(0,0;0,1000;500,1000;500,1500;1000,1500;1000,0)", poly); db::GenericRepository repo; db::PolygonRef ref1 (poly, repo); - db::PolygonRef ref2 (poly.transformed (db::Trans (db::Vector (0, 10))), repo); - db::PolygonRef ref3 (poly.transformed (db::Trans (db::Vector (0, 2000))), repo); - db::PolygonRef ref4 (poly.transformed (db::Trans (db::Vector (0, 1500))), repo); + db::ICplxTrans t2 (db::Trans (db::Vector (0, 10))); + db::PolygonRef ref2 (poly.transformed (t2), repo); + db::ICplxTrans t3 (db::Trans (db::Vector (0, 2000))); + db::PolygonRef ref3 (poly.transformed (t3), repo); + db::ICplxTrans t4 (db::Trans (db::Vector (0, 1500))); + db::PolygonRef ref4 (poly.transformed (t4), repo); EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2), true); // t2*ref1 == ref2 EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2), true); EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2), true); EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3), false); EXPECT_EQ (conn.interacts (ref1, 0, ref4, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t4), true); EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3), false); EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false); } From e918cc74b0bf75a706566e9c3d06dbde3e2322ba Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Dec 2018 10:41:37 +0100 Subject: [PATCH 069/335] WIP: tests for cluster in hierarchical network processor. --- src/db/db/dbHierNetworkProcessor.cc | 17 +++- src/db/db/dbHierNetworkProcessor.h | 5 + .../unit_tests/dbHierNetworkProcessorTests.cc | 94 +++++++++++++++++++ 3 files changed, 112 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index bd48e5800..ac1b9e06b 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -132,6 +132,15 @@ local_cluster::local_cluster () // .. nothing yet .. } +template +void +local_cluster::clear () +{ + m_shapes.clear (); + m_needs_update = false; + m_bbox = box_type (); +} + template void local_cluster::add (const T &s, unsigned int la) @@ -241,7 +250,7 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans { const_cast *> (this)->ensure_sorted (); - if (! other.bbox ().overlaps (bbox ())) { + if (! other.bbox ().touches (bbox ())) { return false; } @@ -253,7 +262,7 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans bool any = false; for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { - for (typename tree_type::overlapping_iterator i = s->second.begin_overlapping (common, bc); ! i.at_end (); ++i) { + for (typename tree_type::touching_iterator i = s->second.begin_touching (common, bc); ! i.at_end (); ++i) { scanner.insert1 (i.operator-> (), s->first); any = true; } @@ -264,13 +273,13 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans } for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { - for (typename tree_type::overlapping_iterator i = s->second.begin_overlapping (common.transformed (trans.inverted ()), bc); ! i.at_end (); ++i) { + for (typename tree_type::touching_iterator i = s->second.begin_touching (common.transformed (trans.inverted ()), bc); ! i.at_end (); ++i) { scanner.insert2 (i.operator-> (), s->first); } } interaction_receiver rec (conn, trans); - return ! scanner.process (rec, 0, bc, bc_t); + return ! scanner.process (rec, 1 /*==touching*/, bc, bc_t); } // explicit instantiations diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 80e53a1cd..699a41d0c 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -125,6 +125,11 @@ public: */ local_cluster (); + /** + * @brief Clears the cluster + */ + void clear (); + /** * @brief Adds a shape with the given layer to the cluster */ diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 23176b8fd..f12581415 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -133,3 +133,97 @@ TEST(2_ShapeInteractionsRealPolygon) EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false); } + +TEST(10_LocalClusterBasic) +{ + db::GenericRepository repo; + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + db::local_cluster cluster; + EXPECT_EQ (cluster.bbox ().to_string (), "()"); + EXPECT_EQ (cluster.id (), size_t (0)); + + cluster.add (db::PolygonRef (poly, repo), 0); + EXPECT_EQ (cluster.bbox ().to_string (), "(0,0;1000,1000)"); + + db::local_cluster cluster2; + cluster.add (db::PolygonRef (poly, repo).transformed (db::Trans (db::Vector (10, 20))), 1); + + cluster.join_with (cluster2); + EXPECT_EQ (cluster.bbox ().to_string (), "(0,0;1010,1020)"); +} + +TEST(11_LocalClusterInteractBasic) +{ + db::GenericRepository repo; + + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + conn.connect (0, 1); + conn.connect (0, 2); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + db::local_cluster cluster; + db::local_cluster cluster2; + + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + + cluster.add (db::PolygonRef (poly, repo), 0); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + + cluster2.add (db::PolygonRef (poly, repo), 0); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn), false); + + cluster.clear (); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); +} + +TEST(11_LocalClusterInteractDifferentLayers) +{ + db::GenericRepository repo; + + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + conn.connect (0, 1); + conn.connect (0, 2); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + db::local_cluster cluster; + db::local_cluster cluster2; + + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + + cluster.add (db::PolygonRef (poly, repo), 0); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + + cluster2.add (db::PolygonRef (poly, repo), 1); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn), false); + + cluster.clear (); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + cluster.add (db::PolygonRef (poly, repo), 2); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); // not connected + + cluster.clear (); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + cluster.add (db::PolygonRef (poly, repo), 1); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true); +} From f7f9f4f1fc44373042d5cf2e89dc8c885a5678e0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Dec 2018 12:55:54 +0100 Subject: [PATCH 070/335] WIP: hier network processor: local cluster set --- src/db/db/dbHierNetworkProcessor.cc | 208 +++++++++++++++--- src/db/db/dbHierNetworkProcessor.h | 126 ++++++++++- .../unit_tests/dbHierNetworkProcessorTests.cc | 96 ++++++++ 3 files changed, 399 insertions(+), 31 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index ac1b9e06b..686520f4a 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -60,13 +60,13 @@ Connectivity::connect (unsigned int l) } Connectivity::layer_iterator -Connectivity::begin_layers () +Connectivity::begin_layers () const { return m_all_layers.begin (); } Connectivity::layer_iterator -Connectivity::end_layers () +Connectivity::end_layers () const { return m_all_layers.end (); } @@ -74,7 +74,7 @@ Connectivity::end_layers () Connectivity::layers_type s_empty_layers; Connectivity::layer_iterator -Connectivity::begin_connected (unsigned int layer) +Connectivity::begin_connected (unsigned int layer) const { std::map::const_iterator i = m_connected.find (layer); if (i == m_connected.end ()) { @@ -85,7 +85,7 @@ Connectivity::begin_connected (unsigned int layer) } Connectivity::layer_iterator -Connectivity::end_connected (unsigned int layer) +Connectivity::end_connected (unsigned int layer) const { std::map::const_iterator i = m_connected.find (layer); if (i == m_connected.end ()) { @@ -244,6 +244,19 @@ private: } +template +typename local_cluster::shape_iterator local_cluster::begin (unsigned int l) const +{ + static tree_type s_empty_tree; + + typename std::map::const_iterator i = m_shapes.find (l); + if (i == m_shapes.end ()) { + return s_empty_tree.begin_flat (); + } else { + return i->second.begin_flat (); + } +} + template bool local_cluster::interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const @@ -287,34 +300,177 @@ template class DB_PUBLIC local_cluster; // ------------------------------------------------------------------------------ -// local_cluster implementation +// local_clusters implementation - -#if 0 -/** - * @brief Represents a collection of clusters in a cell - * - * After all shapes in a cell are connected, the cluster collection is filled if - * disconnected clusters. - */ -class LocalClusters +template +local_clusters::local_clusters () + : m_needs_update (false) { -public: - LocalClusters (); + // .. nothing yet .. +} - // @@@ needs to be fast(!) - LocalCluster::id_type cluster_id_for_shape (const db::Shape &s, unsigned int ls) const; - const LocalCluster &cluster (LocalCluster::id_type id) const; - LocalCluster &create_cluster (); - void remove_cluster (LocalCluster::id_type id); - cluster_iterator find (const db::Box ®ion); +template +void local_clusters::clear () +{ + m_needs_update = false; + m_clusters.clear (); + m_bbox = box_type (); +} - // @@@ Trans is the transformation of the clusters to the shape (instance transformation) - std::vector interacting_clusters (const db::Shape &s, unsigned int ls, const db::ICplxTrans &trans, const Connectivity &conn) const; - // @@@ Trans is the transformation of the clusters to the clusters looked up (instance transformation) - std::vector interacting_clusters (const LocalClusters &c, const db::ICplxTrans &trans, const Connectivity &conn) const; +template +const local_cluster & +local_clusters::cluster_by_id (typename local_cluster::id_type id) const +{ + // by convention the ID is the index + 1 so 0 can be used as "nil" + tl_assert (id > 0 && id <= m_clusters.size ()); + return m_clusters.objects ().item (id - 1); +} + +template +void +local_clusters::remove_cluster (typename local_cluster::id_type id) +{ + tl_assert (id > 0 && id <= m_clusters.size ()); + // TODO: get rid of this const_cast by providing "delete by index" + // m_clusters.erase (id - 1) + local_cluster *to_delete = const_cast *> (& m_clusters.objects ().item (id - 1)); + m_clusters.erase (m_clusters.iterator_from_pointer (to_delete)); + m_needs_update = true; +} + +template +local_cluster * +local_clusters::insert () +{ + typename tree_type::iterator i = m_clusters.insert (local_cluster ()); + i->set_id (i.index () + 1); + m_needs_update = true; + return i.operator-> (); +} + +template +void +local_clusters::ensure_sorted () +{ + if (! m_needs_update) { + return; + } + + // sort the shape trees + m_clusters.sort (local_cluster_box_convert ()); + + // recompute bounding box + m_bbox = box_type (); + for (typename tree_type::const_iterator i = m_clusters.begin (); i != m_clusters.end (); ++i) { + m_bbox += i->bbox (); + } + + m_needs_update = false; +} + +namespace +{ + +template +struct cluster_building_receiver + : public db::box_scanner_receiver +{ + typedef typename local_cluster::id_type id_type; + + cluster_building_receiver (local_clusters &clusters, const db::Connectivity &conn) + : mp_clusters (&clusters), mp_conn (&conn) + { + // .. nothing yet.. + } + + void add (const T *s1, unsigned int l1, const T *s2, unsigned int l2) + { + if (! mp_conn->interacts (*s1, l1, *s2, l2)) { + return; + } + + typename std::map::const_iterator id1 = m_shape_to_cluster_id.find (s1); + typename std::map::const_iterator id2 = m_shape_to_cluster_id.find (s2); + + if (id1 == m_shape_to_cluster_id.end ()) { + + if (id2 == m_shape_to_cluster_id.end ()) { + + local_cluster *cluster = mp_clusters->insert (); + cluster->add (*s1, l1); + cluster->add (*s2, l2); + + m_shape_to_cluster_id.insert (std::make_pair (s1, cluster->id ())); + m_shape_to_cluster_id.insert (std::make_pair (s2, cluster->id ())); + + } else { + + // NOTE: const_cast is in order - we know what we're doing. + const_cast &> (mp_clusters->cluster_by_id (id2->second)).add (*s1, l1); + m_shape_to_cluster_id.insert (std::make_pair (s1, id2->second)); + + } + + } else if (id2 == m_shape_to_cluster_id.end ()) { + + // NOTE: const_cast is in order - we know what we're doing. + const_cast &> (mp_clusters->cluster_by_id (id1->second)).add (*s2, l2); + m_shape_to_cluster_id.insert (std::make_pair (s2, id1->second)); + + } else if (id1->second != id2->second) { + + // this shape connects two clusters: join them + // NOTE: const_cast is in order - we know what we're doing. + const_cast &> (mp_clusters->cluster_by_id (id1->second)).join_with (mp_clusters->cluster_by_id (id2->second)); + mp_clusters->remove_cluster (id2->second); + + } + } + + void finish (const T *s, unsigned int l) + { + // if the shape has not been handled yet, insert a single cluster with only this shape + typename std::map::const_iterator id = m_shape_to_cluster_id.find (s); + if (id == m_shape_to_cluster_id.end ()) { + local_cluster *cluster = mp_clusters->insert (); + cluster->add (*s, l); + } + } + +private: + local_clusters *mp_clusters; + const db::Connectivity *mp_conn; + std::map m_shape_to_cluster_id; }; +} + +template +void +local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) +{ + db::box_scanner bs; + typename T::tag object_tag; + db::box_convert bc; + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + const db::Shapes &shapes = cell.shapes (*l); + for (db::Shapes::shape_iterator s = shapes.begin (shape_flags); ! s.at_end (); ++s) { + bs.insert (s->basic_ptr (object_tag), *l); + } + } + + cluster_building_receiver rec (*this, conn); + bs.process (rec, 1 /*==touching*/, bc); +} + +// explicit instantiations +template class DB_PUBLIC local_clusters; + +// ------------------------------------------------------------------------------ +// hier_clusters implementation + +#if 0 /** * @brief Represents all clusters in a cell and their connections to child cells * diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 699a41d0c..7ae2a6bbd 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -28,6 +28,7 @@ #include "dbTrans.h" #include "dbBoxConvert.h" #include "dbBoxTree.h" +#include "dbCell.h" #include #include @@ -67,22 +68,22 @@ public: /** * @brief Begin iterator for the layers involved */ - layer_iterator begin_layers (); + layer_iterator begin_layers () const; /** * @brief End iterator for the layers involved */ - layer_iterator end_layers (); + layer_iterator end_layers () const; /** * @brief Begin iterator for the layers connected to a specific layer */ - layer_iterator begin_connected (unsigned int layer); + layer_iterator begin_connected (unsigned int layer) const; /** * @brief End iterator for the layers connected to a specific layer */ - layer_iterator end_connected (unsigned int layer); + layer_iterator end_connected (unsigned int layer) const; /** * @brief Returns true, if the given shapes on the given layers interact @@ -119,6 +120,8 @@ class DB_PUBLIC local_cluster public: typedef size_t id_type; typedef typename T::box_type box_type; + typedef db::unstable_box_tree > tree_type; + typedef typename tree_type::flat_iterator shape_iterator; /** * @brief Creates an empty cluster @@ -170,10 +173,14 @@ public: return m_bbox; } + /** + * @brief Gets the shape iterator for a given layer + */ + shape_iterator begin (unsigned int l) const; + private: template friend class local_clusters; template friend class interaction_receiver; - typedef db::unstable_box_tree > tree_type; void set_id (id_type id) { @@ -195,6 +202,115 @@ private: box_type m_bbox; }; +/** + * @brief A box converter for the local_cluster class + */ +template +struct DB_PUBLIC local_cluster_box_convert +{ + typedef typename local_cluster::box_type box_type; + typedef typename db::simple_bbox_tag complexity; + + box_type operator() (const local_cluster &c) const + { + return c.bbox (); + } +}; + +/** + * @brief A collection of clusters + * + * Clusters are identified by their ID. This collection + * supports cluster lookup by a box region and building + * the clusters from a cell's shapes. + */ +template +class DB_PUBLIC local_clusters +{ +public: + typedef typename local_cluster::box_type box_type; + typedef db::box_tree, local_cluster_box_convert > tree_type; + typedef typename tree_type::touching_iterator touching_iterator; + typedef typename tree_type::const_iterator const_iterator; + + /** + * @brief Creates an empty collection + */ + local_clusters (); + + /** + * @brief Gets the cluster by ID + */ + const local_cluster &cluster_by_id (typename local_cluster::id_type id) const; + + /** + * @brief Clears the clusters + */ + void clear (); + + /** + * @brief Removes a cluster with the given ID + */ + void remove_cluster (typename local_cluster::id_type id); + + /** + * @brief Gets the bounding box of the clusters + */ + box_type bbox () const + { + const_cast *> (this)->ensure_sorted (); + return m_bbox; + } + + /** + * @brief Gets the clusters (begin iterator) + */ + const_iterator begin () const + { + return m_clusters.begin (); + } + + /** + * @brief Gets the clusters (end iterator) + */ + const_iterator end () const + { + return m_clusters.end (); + } + + /** + * @brief Gets the clusters touching a given region + */ + touching_iterator begin_touching (const box_type &box) const + { + const_cast *> (this)->ensure_sorted (); + return m_clusters.begin_touching (box, local_cluster_box_convert ()); + } + + /** + * @brief Builds this collection from a cell and the given connectivity + * + * This method will only build the local clusters. Child cells + * are not taken into account. Only the shape types listed in + * shape_flags are taken. + */ + void build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); + + /** + * @brief Creates and inserts a new clusters + * + * NOTE: the object should not be modified after sorting has taken place. + */ + local_cluster *insert (); + +private: + void ensure_sorted (); + + bool m_needs_update; + box_type m_bbox; + tree_type m_clusters; +}; + } #endif diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index f12581415..4cc87f51a 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -28,6 +28,7 @@ #include "dbPolygon.h" #include "dbPath.h" #include "dbText.h" +#include "dbLayout.h" static std::string l2s (db::Connectivity::layer_iterator b, db::Connectivity::layer_iterator e) { @@ -227,3 +228,98 @@ TEST(11_LocalClusterInteractDifferentLayers) cluster.add (db::PolygonRef (poly, repo), 1); EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true); } + +static std::string obj2string (const db::PolygonRef &ref) +{ + return ref.obj ().transformed (ref.trans ()).to_string (); +} + +template +static std::string local_cluster_to_string (const db::local_cluster &cluster, const db::Connectivity &conn) +{ + std::string res; + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + for (typename db::local_cluster::shape_iterator s = cluster.begin (*l); ! s.at_end (); ++s) { + if (! res.empty ()) { + res += ";"; + } + res += "[" + tl::to_string (*l) + "]" + obj2string (*s); + } + } + return res; +} + +template +static std::string local_clusters_to_string (const db::local_clusters &clusters, const db::Connectivity &conn) +{ + std::string s; + for (typename db::local_clusters::const_iterator c = clusters.begin (); c != clusters.end (); ++c) { + if (! s.empty ()) { + s += "\n"; + } + s += "#" + tl::to_string (c->id ()) + ":" + local_cluster_to_string (*c, conn); + } + return s; +} + +TEST(20_LocalClustersBasic) +{ + db::Layout layout; + db::Cell &cell = layout.cell (layout.add_cell ("TOP")); + db::GenericRepository &repo = layout.shape_repository (); + + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + conn.connect (0, 1); + conn.connect (0, 2); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + cell.shapes (0).insert (db::PolygonRef (poly, repo)); + + db::local_clusters clusters; + EXPECT_EQ (local_clusters_to_string (clusters, conn), ""); + + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0)"); + + // one more shape + cell.shapes (0).insert (db::PolygonRef (poly.transformed (db::Trans (db::Vector (10, 20))), repo)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)"); + + // one more shape creating a new cluster + cell.shapes (2).insert (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1100))), repo)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)\n" + "#2:[2](0,1100;0,2100;1000,2100;1000,1100)" + ); + + // one more shape connecting these + cell.shapes (2).insert (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1000))), repo)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20);[2](0,1000;0,2000;1000,2000;1000,1000);[2](0,1100;0,2100;1000,2100;1000,1100)" + ); + + // one more shape opening a new cluster + cell.shapes (1).insert (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1100))), repo)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20);[2](0,1000;0,2000;1000,2000;1000,1000);[2](0,1100;0,2100;1000,2100;1000,1100)\n" + "#2:[1](0,1100;0,2100;1000,2100;1000,1100)" + ); +} + From 0e4eab2dced73b316704995f673ee1c71196d831 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Dec 2018 22:26:03 +0100 Subject: [PATCH 071/335] WIP: hierarchical net clusters. --- src/db/db/dbHierNetworkProcessor.cc | 637 ++++++++++++++++++++++++++-- 1 file changed, 608 insertions(+), 29 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 686520f4a..d26f32bb3 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -470,44 +470,623 @@ template class DB_PUBLIC local_clusters; // ------------------------------------------------------------------------------ // hier_clusters implementation -#if 0 /** - * @brief Represents all clusters in a cell and their connections to child cells - * - * Connections to indirect children are made through empty dummy clusters. - * Also, connections between two clusters of different children without - * mediating cluster on cell level are made through empty dummy clusters. - * Hence, there is always a cluster in the parent cell which connects to - * clusters from child cells. + * @brief A connection to a cluster in a child instance */ -class HierarchicalClusters +class DB_PUBLIC ClusterInstance { public: - HierarchicalClusters (); + typedef std::vector inst_path_type; - LocalClusters &local (); + ClusterInstance (size_t id, const inst_path_type &inst_path) + : m_id (id), m_inst_path (inst_path) + { + // .. nothing yet .. + } - // build local clusters - // determine local to cell cluster interactions -> top-down connection, maybe across dummy - // identify cell overlaps - // determine cell-to-cell cluster interactions -> make dummy cluster to connect both, connect to each child - // maybe across dummy cluster - // shall be called bottom-up - void build_clusters (const db::Cell &cell, const Connectivity &conn); + /** + * @brief Gets the cluster ID + */ + size_t id () const + { + return m_id; + } - // @@@ trans is the transformation from child to this (instance transformation) - // used by child clusters to verify whether they are connected somewhere in the parent - LocalCluster::id_type is_connected_to (LocalCluster::id_type id, const db::InstElement &trans) const; + /** + * @brief Gets the instance path + */ + const inst_path_type &inst () const + { + return m_inst_path; + } - // connections to subcells (== pins?) - const std::vector > &top_down_connections (LocalCluster::id_type) const; + /** + * @brief Equality + */ + bool operator== (const ClusterInstance &other) const + { + return m_id == other.m_id && m_inst_path == other.m_inst_path; + } - // propagate all connected clusters to their parents - // -> creates new local clusters, removes them from their local set - // used by the merge step to form local-only clusters - // can be called bottom-up - void propagate_connected (); + /** + * @brief Less operator + */ + bool operator< (const ClusterInstance &other) const + { + if (m_id != other.m_id) { + return m_id < other.m_id; + } + return m_inst_path < other.m_inst_path; + } + +private: + size_t m_id; + inst_path_type m_inst_path; +}; + +template class hier_clusters; + +/** + * @brief Local clusters with connections to clusters from child cells + */ +template +class DB_PUBLIC connected_clusters + : public local_clusters +{ +public: + typedef std::list connections_type; + typedef typename local_clusters::box_type box_type; + + /** + * @brief Constructor + */ + connected_clusters () + : local_clusters () + { + // .. nothing yet .. + } + + /** + * @brief Gets the connections for a given cluster ID + */ + const connections_type &connections_for_cluster (typename local_cluster::id_type id); + + /** + * @brief Adds a connection between a local cluster and one from a child instance + */ + void add_connection (typename local_cluster::id_type, const ClusterInstance &inst); + + /** + * @brief Joins all connections of with_id to id + */ + void join_connected_clusters (typename local_cluster::id_type id, typename local_cluster::id_type with_id); + + /** + * @brief Reverse "connections_for_cluster" + * + * Finds the cluster which has a connection given by inst. "strip" elements + * are stipped from the front of the instantiation path. + * This method returns 0 if no cluster can be found. + */ + typename local_cluster::id_type find_cluster_with_connection (const ClusterInstance &inst, size_t strip) const; + +private: + std::map::id_type, connections_type> m_connections; + box_type m_full_bbox; +}; + +template +const typename connected_clusters::connections_type & +connected_clusters::connections_for_cluster (typename local_cluster::id_type id) +{ + typename std::map::id_type, connections_type>::const_iterator c = m_connections.find (id); + if (c == m_connections.end ()) { + static connections_type empty_connections; + return empty_connections; + } else { + return c->second; + } +} + +template +void +connected_clusters::add_connection (typename local_cluster::id_type id, const ClusterInstance &inst) +{ + m_connections [id].push_back (inst); +} + +template +void +connected_clusters::join_connected_clusters (typename local_cluster::id_type id, typename local_cluster::id_type with_id) +{ + if (id == with_id) { + return; + } + + const connections_type &to_join = connections_for_cluster (with_id); + connections_type &target = m_connections [id]; + target.insert (target.end (), to_join.begin (), to_join.end ()); + m_connections.erase (with_id); +} + +template +typename local_cluster::id_type +connected_clusters::find_cluster_with_connection (const ClusterInstance &ci, size_t strip) const +{ + for (typename std::map::id_type, connections_type>::const_iterator i = m_connections.begin (); i != m_connections.end (); ++i) { + + for (connections_type::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { + + const ClusterInstance::inst_path_type &path = j->inst (); + if (j->id () == ci.id () && path.size () == ci.inst ().size () - strip) { + + bool match = true; + for (size_t k = 0; k < path.size () && match; ++k) { + match = (path [k] == ci.inst () [k + strip]); + } + if (match) { + return i->first; + } + + } + + } + + } + + return 0; +} + +template class cell_box_converter; + +/** + * @brief A hierarchical representation of clusters + * + * Hierarchical clusters + */ +template +class DB_PUBLIC hier_clusters +{ +public: + typedef typename local_cluster::box_type box_type; + + /** + * @brief Creates an empty set of clusters + */ + hier_clusters (); + + /** + * @brief Builds a hierarchy of clusters from a cell hierarchy and given connectivity + */ + void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); + + /** + * @brief Gets the connected clusters for a given cell + */ + const connected_clusters &clusters_per_cell (db::cell_index_type cell_index) const; + + /** + * @brief Clears this collection + */ + void clear (); + +private: + void do_build (cell_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); + + std::map > m_per_cell_clusters; +}; + +template +class DB_PUBLIC cell_box_converter +{ +public: + typedef db::simple_bbox_tag complexity; + typedef typename hier_clusters::box_type box_type; + + cell_box_converter (const db::Layout &layout, const hier_clusters &tree) + : mp_layout (&layout), mp_tree (&tree) + { + // .. nothing yet .. + } + + const box_type &operator() (db::cell_index_type cell_index) const + { + typename std::map::const_iterator b = m_cache.find (cell_index); + if (b != m_cache.end ()) { + + return b->second; + + } else { + + const connected_clusters &clusters = mp_tree->clusters_per_cell (cell_index); + box_type box = clusters.bbox (); + + const db::Cell &cell = mp_layout->cell (cell_index); + for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { + const db::CellInstArray &inst_array = inst->cell_inst (); + box += inst_array.raw_bbox () * (*this) (inst_array.object ().cell_index ()); + } + + return m_cache.insert (std::make_pair (cell_index, box)).first->second; + + } + } + +private: + mutable std::map m_cache; + const db::Layout *mp_layout; + const hier_clusters *mp_tree; +}; + +template +hier_clusters::hier_clusters () +{ + // .. nothing yet .. +} + +template +void hier_clusters::clear () +{ + m_per_cell_clusters.clear (); +} + +template +void +hier_clusters::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) +{ + clear (); + cell_box_converter cbc (layout, *this); + do_build (cbc, layout, cell, shape_flags, conn); +} + +namespace +{ + +template +struct hc_receiver + : public db::box_scanner_receiver, + public db::box_scanner_receiver2, unsigned int, db::Instance, unsigned int> +{ +public: + typedef typename hier_clusters::box_type box_type; + typedef typename local_cluster::id_type id_type; + typedef std::map *> connector_map; + + hc_receiver (const db::Layout &layout, db::connected_clusters &cell_clusters, hier_clusters &tree, const cell_box_converter &cbc, const db::Connectivity &conn) + : mp_layout (&layout), mp_tree (&tree), mp_cbc (&cbc), mp_conn (&conn) + { + mp_cell_clusters = &cell_clusters; + } + + void add (const db::Instance *i1, unsigned int /*p1*/, const db::Instance *i2, unsigned int /*p2*/) + { + std::vector p; + db::ICplxTrans t; + add_pair (*i1, p, t, *i2, p, t); + } + + void add (const local_cluster *c1, unsigned int /*p1*/, const db::Instance *i2, unsigned int /*p2*/) + { + std::vector p; + db::ICplxTrans t; + add_pair (*c1, *i2, p, t); + } + + bool stop () const + { + return false; + } + +private: + const db::Layout *mp_layout; + db::connected_clusters *mp_cell_clusters; + hier_clusters *mp_tree; + const cell_box_converter *mp_cbc; + const db::Connectivity *mp_conn; + connector_map m_connectors; + mutable std::map m_reduction_cache; + + void add_pair (const db::Instance &i1, const std::vector &p1, const db::ICplxTrans &t1, const db::Instance &i2, const std::vector &p2, const db::ICplxTrans &t2) + { + box_type bb1 = (*mp_cbc) (i1.cell_index ()); + box_type b1 = (i1.cell_inst ().raw_bbox () * bb1).transformed (t1); + + box_type bb2 = (*mp_cbc) (i2.cell_index ()); + box_type b2 = (i2.cell_inst ().raw_bbox () * bb2).transformed (t2); + + if (! b1.touches (b2)) { + return; + } + + db::ICplxTrans t2i = t2.inverted (); + db::ICplxTrans t12 = t2i * t1; + + box_type common = b1 & b2; + + for (db::CellInstArray::iterator ii1 = i1.begin_touching (common, mp_layout); ! ii1.at_end (); ++ii1) { + + db::ICplxTrans tt1 = t1 * i1.complex_trans (*ii1); + box_type ib1 = bb1.transformed (tt1); + box_type ib12 = ib1.transformed (t12); + + ClusterInstance::inst_path_type pp1; + pp1.reserve (p1.size () + 1); + pp1.insert (pp1.end (), p1.begin (), p1.end ()); + pp1.push_back (db::InstElement (i1, ii1)); + + for (db::CellInstArray::iterator ii2 = i2.begin_touching (ib12, mp_layout); ! ii2.at_end (); ++ii2) { + + db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); + box_type ib2 = bb2.transformed (tt2); + + if (ib1.touches (ib2)) { + + ClusterInstance::inst_path_type pp2; + pp2.reserve (p2.size () + 1); + pp2.insert (pp2.end (), p2.begin (), p2.end ()); + pp2.push_back (db::InstElement (i2, ii2)); + + add_single_pair (common, i1.cell_index (), pp1, tt1, i2.cell_index (), pp2, tt2); + + // dive into cell of ii2 + const db::Cell &cell2 = mp_layout->cell (i2.cell_index ()); + for (db::Cell::touching_iterator jj2 = cell2.begin_touching (common.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { + add_pair (i1, p1, t1, *jj2, pp2, tt2); + } + + } + + } + + // dive into cell of ii1 + const db::Cell &cell1 = mp_layout->cell (i1.cell_index ()); + for (db::Cell::touching_iterator jj1 = cell1.begin_touching (common.transformed (tt1.inverted ())); ! jj1.at_end (); ++jj1) { + add_pair (*jj1, pp1, tt1, i2, p2, t2); + } + + } + } + + void add_single_pair (const box_type &common, + db::cell_index_type ci1, const std::vector &p1, const db::ICplxTrans &t1, + db::cell_index_type ci2, const std::vector &p2, const db::ICplxTrans &t2) + { + const db::local_clusters &cl1 = mp_tree->clusters_per_cell (ci1); + const db::local_clusters &cl2 = mp_tree->clusters_per_cell (ci2); + + db::ICplxTrans t1i = t1.inverted (); + db::ICplxTrans t2i = t2.inverted (); + db::ICplxTrans t21 = t1i * t2; + + for (typename db::local_clusters::touching_iterator i = cl1.begin_touching (common.transformed (t1i)); ! i.at_end (); ++i) { + + box_type bc1 = common & i->bbox ().transformed (t1); + for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (bc1.transformed (t2i)); ! j.at_end (); ++j) { + + if (i->interacts (*j, t21, *mp_conn)) { + + ClusterInstance k1 (i->id (), p1); + reduce (k1); + + ClusterInstance k2 (j->id (), p2); + reduce (k2); + + typename connector_map::iterator x1 = m_connectors.find (k1); + typename connector_map::iterator x2 = m_connectors.find (k2); + + if (x1 == m_connectors.end ()) { + + if (x2 == m_connectors.end ()) { + + db::local_cluster *connector = mp_cell_clusters->insert (); + m_connectors [k1] = connector; + mp_cell_clusters->add_connection (connector->id (), k1); + mp_cell_clusters->add_connection (connector->id (), k2); + + } else { + mp_cell_clusters->add_connection (x2->second->id (), k1); + } + + } else if (x2 == m_connectors.end ()) { + + mp_cell_clusters->add_connection (x1->second->id (), k2); + + } else if (x1->second != x2->second) { + + mp_cell_clusters->join_connected_clusters (x1->second->id (), x2->second->id ()); + mp_cell_clusters->remove_cluster (x2->second->id ()); + x2->second = x1->second; + + } + + } + + } + + } + + } + + void add_pair (const local_cluster &c1, const db::Instance &i2, const std::vector &p2, const db::ICplxTrans &t2) + { + box_type b1 = c1.bbox (); + + box_type bb2 = (*mp_cbc) (i2.cell_index ()); + box_type b2 = (i2.cell_inst ().raw_bbox () * bb2).transformed (t2); + + if (! b1.touches (b2)) { + return; + } + + box_type common = b1 & b2; + + for (db::CellInstArray::iterator ii2 = i2.begin_touching (common.transformed (t2.inverted ()), mp_layout); ! ii2.at_end (); ++ii2) { + + db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); + box_type ib2 = bb2.transformed (tt2); + + if (b1.touches (ib2)) { + + ClusterInstance::inst_path_type pp2; + pp2.reserve (p2.size () + 1); + pp2.insert (pp2.end (), p2.begin (), p2.end ()); + pp2.push_back (db::InstElement (i2, ii2)); + + add_single_pair (c1, i2.cell_index (), pp2, tt2); + + // dive into cell of ii2 + const db::Cell &cell2 = mp_layout->cell (i2.cell_index ()); + for (db::Cell::touching_iterator jj2 = cell2.begin_touching (common.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { + add_pair (c1, *jj2, pp2, tt2); + } + + } + + } + } + + void add_single_pair (const local_cluster &c1, + db::cell_index_type ci2, const std::vector &p2, const db::ICplxTrans &t2) + { + const db::local_clusters &cl2 = mp_tree->clusters_per_cell (ci2); + + for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (c1.bbox ().transformed (t2.inverted ())); ! j.at_end (); ++j) { + + if (c1.interacts (*j, t2, *mp_conn)) { + + ClusterInstance k2 (j->id (), p2); + reduce (k2); + + mp_cell_clusters->add_connection (c1.id (), k2); + + } + + } + } + + /** + * @brief Reduce the connection path to the connector highest in the hierarchy + * + * This feature shall prevent a situation where a connection is made to a sub-cluster of + * another hierarchical cluster. We always attach to the highest possible connector. + */ + void reduce (ClusterInstance &k) const + { + std::map::const_iterator kr = m_reduction_cache.find (k); + if (kr != m_reduction_cache.end ()) { + k = kr->second; + return; + } + + for (size_t rn = 1; rn + 1 < k.inst ().size (); ++rn) { + + const db::connected_clusters &clusters = mp_tree->clusters_per_cell (k.inst () [rn - 1].inst_ptr.cell_index ()); + id_type red_id = clusters.find_cluster_with_connection (k, rn); + if (red_id > 0) { + + // reduction possible + ClusterInstance::inst_path_type new_path; + new_path.insert (new_path.end (), k.inst ().begin (), k.inst ().begin () + rn); + k = ClusterInstance (red_id, new_path); + return; + + } + + } + + } +}; + +template +struct cell_inst_clusters_box_converter +{ + typedef typename local_cluster::box_type box_type; + typedef db::simple_bbox_tag complexity; + + cell_inst_clusters_box_converter (const cell_box_converter &cbc) + : mp_cbc (&cbc) + { + // .. nothing yet .. + } + + box_type operator() (const db::Instance &inst) const + { + return inst.cell_inst ().raw_bbox () * (*mp_cbc) (inst.cell_index ()); + } + +private: + const cell_box_converter *mp_cbc; }; -#endif + +} + +template +void +hier_clusters::do_build (cell_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) +{ + // already done - don't do again + if (m_per_cell_clusters.find (cell.cell_index ()) != m_per_cell_clusters.end ()) { + return; + } + + // Build local clusters + + connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; + local.build_clusters (cell, shape_flags, conn); + + // handle connections inside child cells in a bottom-up fashion + + for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end (); ++cc) { + do_build (cbc, layout, layout.cell (*cc), shape_flags, conn); + } + + // NOTE: this is a receiver for both the child-to-child and + // local to child interactions. + hc_receiver rec (layout, local, *this, cbc, conn); + cell_inst_clusters_box_converter cibc (cbc); + + // handle instance to instance connections + + { + db::box_scanner bs; + + for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { + bs.insert (inst.operator-> (), 0); + } + + bs.process (rec, 1 /*touching*/, cibc); + } + + // handle local to instance connections + + { + db::box_scanner2, unsigned int, db::Instance, unsigned int> bs2; + + for (typename connected_clusters::const_iterator c = local.begin (); c != local.end (); ++c) { + bs2.insert1 (c.operator-> (), 0); + } + for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { + bs2.insert2 (inst.operator-> (), 0); + } + + bs2.process (rec, 1 /*touching*/, local_cluster_box_convert (), cibc); + } +} + +template +const connected_clusters & +hier_clusters::clusters_per_cell (db::cell_index_type cell_index) const +{ + typename std::map >::const_iterator c = m_per_cell_clusters.find (cell_index); + if (c == m_per_cell_clusters.end ()) { + static connected_clusters empty; + return empty; + } else { + return c->second; + } +} + +// explicit instantiations +template class DB_PUBLIC hier_clusters; } From 22651b83c045fa5304bf6a990e0219b93d907c39 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Dec 2018 22:39:53 +0100 Subject: [PATCH 072/335] WIP: hierarchical net clusters - needs testing --- src/db/db/dbHierNetworkProcessor.cc | 217 +++++++++------------------- src/db/db/dbHierNetworkProcessor.h | 145 +++++++++++++++++++ 2 files changed, 210 insertions(+), 152 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index d26f32bb3..315095ed2 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -468,112 +468,7 @@ local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flag template class DB_PUBLIC local_clusters; // ------------------------------------------------------------------------------ -// hier_clusters implementation - -/** - * @brief A connection to a cluster in a child instance - */ -class DB_PUBLIC ClusterInstance -{ -public: - typedef std::vector inst_path_type; - - ClusterInstance (size_t id, const inst_path_type &inst_path) - : m_id (id), m_inst_path (inst_path) - { - // .. nothing yet .. - } - - /** - * @brief Gets the cluster ID - */ - size_t id () const - { - return m_id; - } - - /** - * @brief Gets the instance path - */ - const inst_path_type &inst () const - { - return m_inst_path; - } - - /** - * @brief Equality - */ - bool operator== (const ClusterInstance &other) const - { - return m_id == other.m_id && m_inst_path == other.m_inst_path; - } - - /** - * @brief Less operator - */ - bool operator< (const ClusterInstance &other) const - { - if (m_id != other.m_id) { - return m_id < other.m_id; - } - return m_inst_path < other.m_inst_path; - } - -private: - size_t m_id; - inst_path_type m_inst_path; -}; - -template class hier_clusters; - -/** - * @brief Local clusters with connections to clusters from child cells - */ -template -class DB_PUBLIC connected_clusters - : public local_clusters -{ -public: - typedef std::list connections_type; - typedef typename local_clusters::box_type box_type; - - /** - * @brief Constructor - */ - connected_clusters () - : local_clusters () - { - // .. nothing yet .. - } - - /** - * @brief Gets the connections for a given cluster ID - */ - const connections_type &connections_for_cluster (typename local_cluster::id_type id); - - /** - * @brief Adds a connection between a local cluster and one from a child instance - */ - void add_connection (typename local_cluster::id_type, const ClusterInstance &inst); - - /** - * @brief Joins all connections of with_id to id - */ - void join_connected_clusters (typename local_cluster::id_type id, typename local_cluster::id_type with_id); - - /** - * @brief Reverse "connections_for_cluster" - * - * Finds the cluster which has a connection given by inst. "strip" elements - * are stipped from the front of the instantiation path. - * This method returns 0 if no cluster can be found. - */ - typename local_cluster::id_type find_cluster_with_connection (const ClusterInstance &inst, size_t strip) const; - -private: - std::map::id_type, connections_type> m_connections; - box_type m_full_bbox; -}; +// connected_clusters implementation template const typename connected_clusters::connections_type & @@ -637,53 +532,17 @@ connected_clusters::find_cluster_with_connection (const ClusterInstance &ci, return 0; } -template class cell_box_converter; - -/** - * @brief A hierarchical representation of clusters - * - * Hierarchical clusters - */ -template -class DB_PUBLIC hier_clusters -{ -public: - typedef typename local_cluster::box_type box_type; - - /** - * @brief Creates an empty set of clusters - */ - hier_clusters (); - - /** - * @brief Builds a hierarchy of clusters from a cell hierarchy and given connectivity - */ - void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); - - /** - * @brief Gets the connected clusters for a given cell - */ - const connected_clusters &clusters_per_cell (db::cell_index_type cell_index) const; - - /** - * @brief Clears this collection - */ - void clear (); - -private: - void do_build (cell_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); - - std::map > m_per_cell_clusters; -}; +// ------------------------------------------------------------------------------ +// connected_clusters implementation template -class DB_PUBLIC cell_box_converter +class DB_PUBLIC cell_clusters_box_converter { public: typedef db::simple_bbox_tag complexity; typedef typename hier_clusters::box_type box_type; - cell_box_converter (const db::Layout &layout, const hier_clusters &tree) + cell_clusters_box_converter (const db::Layout &layout, const hier_clusters &tree) : mp_layout (&layout), mp_tree (&tree) { // .. nothing yet .. @@ -718,6 +577,9 @@ private: const hier_clusters *mp_tree; }; +// ------------------------------------------------------------------------------ +// hier_clusters implementation + template hier_clusters::hier_clusters () { @@ -735,13 +597,21 @@ void hier_clusters::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) { clear (); - cell_box_converter cbc (layout, *this); + cell_clusters_box_converter cbc (layout, *this); do_build (cbc, layout, cell, shape_flags, conn); } namespace { +/** + * @brief The central interaction tester between clusters on a hierarchical level + * + * This receiver is both used for the instance-to-instance and the local-to-instance + * interactions. It is employed on cell level for in two box scanners: one + * investigating the instance-to-instance interactions and another one invesitating + * local cluster to instance interactions. + */ template struct hc_receiver : public db::box_scanner_receiver, @@ -752,12 +622,18 @@ public: typedef typename local_cluster::id_type id_type; typedef std::map *> connector_map; - hc_receiver (const db::Layout &layout, db::connected_clusters &cell_clusters, hier_clusters &tree, const cell_box_converter &cbc, const db::Connectivity &conn) + /** + * @brief Constructor + */ + hc_receiver (const db::Layout &layout, db::connected_clusters &cell_clusters, hier_clusters &tree, const cell_clusters_box_converter &cbc, const db::Connectivity &conn) : mp_layout (&layout), mp_tree (&tree), mp_cbc (&cbc), mp_conn (&conn) { mp_cell_clusters = &cell_clusters; } + /** + * @brief Receiver main event for instance-to-instance interactions + */ void add (const db::Instance *i1, unsigned int /*p1*/, const db::Instance *i2, unsigned int /*p2*/) { std::vector p; @@ -765,6 +641,9 @@ public: add_pair (*i1, p, t, *i2, p, t); } + /** + * @brief Receiver main event for local-to-instance interactions + */ void add (const local_cluster *c1, unsigned int /*p1*/, const db::Instance *i2, unsigned int /*p2*/) { std::vector p; @@ -781,11 +660,21 @@ private: const db::Layout *mp_layout; db::connected_clusters *mp_cell_clusters; hier_clusters *mp_tree; - const cell_box_converter *mp_cbc; + const cell_clusters_box_converter *mp_cbc; const db::Connectivity *mp_conn; connector_map m_connectors; mutable std::map m_reduction_cache; + /** + * @brief Handles the cluster interactions between two instances or instance arrays + * @param common The region under investigation (seen from the top level) + * @param i1 The index of the child cell 1 + * @param p1 The instantiation path to the child cell (not including i1) + * @param t1 The accumulated transformation of the path, not including i1 + * @param i2 The index of the child cell 2 + * @param p2 The instantiation path to the child cell (not including i2) + * @param t2 The accumulated transformation of the path, not including i2 + */ void add_pair (const db::Instance &i1, const std::vector &p1, const db::ICplxTrans &t1, const db::Instance &i2, const std::vector &p2, const db::ICplxTrans &t2) { box_type bb1 = (*mp_cbc) (i1.cell_index ()); @@ -847,6 +736,16 @@ private: } } + /** + * @brief Handles the cluster interactions between two specific instances + * @param common The region under investigation (seen from the top level) + * @param ci1 The cell index of the child cell 1 + * @param p1 The instantiation path to the child cell (last element is the instance to ci1) + * @param t1 The accumulated transformation of the path p1 + * @param ci2 The cell index of the child cell 2 + * @param p2 The instantiation path to the child cell (last element is the instance to ci2) + * @param t2 The accumulated transformation of the path p2 + */ void add_single_pair (const box_type &common, db::cell_index_type ci1, const std::vector &p1, const db::ICplxTrans &t1, db::cell_index_type ci2, const std::vector &p2, const db::ICplxTrans &t2) @@ -907,6 +806,13 @@ private: } + /** + * @brief Handles a local clusters vs. the clusters of a specific child instance or instance array + * @param c1 The local cluster + * @param i2 The index of the child cell + * @param p2 The instantiation path to the child cell (not including i2) + * @param t2 The accumulated transformation of the path, not including i2 + */ void add_pair (const local_cluster &c1, const db::Instance &i2, const std::vector &p2, const db::ICplxTrans &t2) { box_type b1 = c1.bbox (); @@ -945,6 +851,13 @@ private: } } + /** + * @brief Handles a local clusters vs. the clusters of a specific child instance + * @param c1 The local cluster + * @param ci2 The cell index of the child cell + * @param p2 The instantiation path to the child cell (last element is the instance to ci2) + * @param t2 The accumulated transformation of the path + */ void add_single_pair (const local_cluster &c1, db::cell_index_type ci2, const std::vector &p2, const db::ICplxTrans &t2) { @@ -1003,7 +916,7 @@ struct cell_inst_clusters_box_converter typedef typename local_cluster::box_type box_type; typedef db::simple_bbox_tag complexity; - cell_inst_clusters_box_converter (const cell_box_converter &cbc) + cell_inst_clusters_box_converter (const cell_clusters_box_converter &cbc) : mp_cbc (&cbc) { // .. nothing yet .. @@ -1015,14 +928,14 @@ struct cell_inst_clusters_box_converter } private: - const cell_box_converter *mp_cbc; + const cell_clusters_box_converter *mp_cbc; }; } template void -hier_clusters::do_build (cell_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) +hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) { // already done - don't do again if (m_per_cell_clusters.find (cell.cell_index ()) != m_per_cell_clusters.end ()) { diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 7ae2a6bbd..df7b37af0 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -29,6 +29,7 @@ #include "dbBoxConvert.h" #include "dbBoxTree.h" #include "dbCell.h" +#include "dbInstElement.h" #include #include @@ -311,6 +312,150 @@ private: tree_type m_clusters; }; +/** + * @brief A connection to a cluster in a child instance + */ +class DB_PUBLIC ClusterInstance +{ +public: + typedef std::vector inst_path_type; + + ClusterInstance (size_t id, const inst_path_type &inst_path) + : m_id (id), m_inst_path (inst_path) + { + // .. nothing yet .. + } + + /** + * @brief Gets the cluster ID + */ + size_t id () const + { + return m_id; + } + + /** + * @brief Gets the instance path + */ + const inst_path_type &inst () const + { + return m_inst_path; + } + + /** + * @brief Equality + */ + bool operator== (const ClusterInstance &other) const + { + return m_id == other.m_id && m_inst_path == other.m_inst_path; + } + + /** + * @brief Less operator + */ + bool operator< (const ClusterInstance &other) const + { + if (m_id != other.m_id) { + return m_id < other.m_id; + } + return m_inst_path < other.m_inst_path; + } + +private: + size_t m_id; + inst_path_type m_inst_path; +}; + +template class hier_clusters; + +/** + * @brief Local clusters with connections to clusters from child cells + */ +template +class DB_PUBLIC connected_clusters + : public local_clusters +{ +public: + typedef std::list connections_type; + typedef typename local_clusters::box_type box_type; + + /** + * @brief Constructor + */ + connected_clusters () + : local_clusters () + { + // .. nothing yet .. + } + + /** + * @brief Gets the connections for a given cluster ID + */ + const connections_type &connections_for_cluster (typename local_cluster::id_type id); + + /** + * @brief Adds a connection between a local cluster and one from a child instance + */ + void add_connection (typename local_cluster::id_type, const ClusterInstance &inst); + + /** + * @brief Joins all connections of with_id to id + */ + void join_connected_clusters (typename local_cluster::id_type id, typename local_cluster::id_type with_id); + + /** + * @brief Reverse "connections_for_cluster" + * + * Finds the cluster which has a connection given by inst. "strip" elements + * are stipped from the front of the instantiation path. + * This method returns 0 if no cluster can be found. + */ + typename local_cluster::id_type find_cluster_with_connection (const ClusterInstance &inst, size_t strip) const; + +private: + std::map::id_type, connections_type> m_connections; + box_type m_full_bbox; +}; + +template class cell_clusters_box_converter; + +/** + * @brief A hierarchical representation of clusters + * + * Hierarchical clusters + */ +template +class DB_PUBLIC hier_clusters +{ +public: + typedef typename local_cluster::box_type box_type; + + /** + * @brief Creates an empty set of clusters + */ + hier_clusters (); + + /** + * @brief Builds a hierarchy of clusters from a cell hierarchy and given connectivity + */ + void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); + + /** + * @brief Gets the connected clusters for a given cell + */ + const connected_clusters &clusters_per_cell (db::cell_index_type cell_index) const; + + /** + * @brief Clears this collection + */ + void clear (); + +private: + void do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); + + std::map > m_per_cell_clusters; +}; + } #endif From 7e36018356d9d1fde7a12aaad6b6c85ad8a2fbe5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Dec 2018 23:05:58 +0100 Subject: [PATCH 073/335] WIP: hierarchical net clusters - some testing --- src/db/db/dbHierNetworkProcessor.cc | 5 +- src/db/db/dbHierNetworkProcessor.h | 3 +- .../unit_tests/dbHierNetworkProcessorTests.cc | 69 +++++++++++++++++++ 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 315095ed2..53a1870e8 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -472,7 +472,7 @@ template class DB_PUBLIC local_clusters; template const typename connected_clusters::connections_type & -connected_clusters::connections_for_cluster (typename local_cluster::id_type id) +connected_clusters::connections_for_cluster (typename local_cluster::id_type id) const { typename std::map::id_type, connections_type>::const_iterator c = m_connections.find (id); if (c == m_connections.end ()) { @@ -532,6 +532,9 @@ connected_clusters::find_cluster_with_connection (const ClusterInstance &ci, return 0; } +// explicit instantiations +template class DB_PUBLIC connected_clusters; + // ------------------------------------------------------------------------------ // connected_clusters implementation diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index df7b37af0..3964cc76b 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -391,7 +391,7 @@ public: /** * @brief Gets the connections for a given cluster ID */ - const connections_type &connections_for_cluster (typename local_cluster::id_type id); + const connections_type &connections_for_cluster (typename local_cluster::id_type id) const; /** * @brief Adds a connection between a local cluster and one from a child instance @@ -414,7 +414,6 @@ public: private: std::map::id_type, connections_type> m_connections; - box_type m_full_bbox; }; template class cell_clusters_box_converter; diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 4cc87f51a..c3911ed38 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -323,3 +323,72 @@ TEST(20_LocalClustersBasic) ); } +TEST(30_LocalConnectedClusters) +{ + db::Layout layout; + db::cell_index_type ci1 = layout.add_cell ("C1"); + db::cell_index_type ci2 = layout.add_cell ("C2"); + db::cell_index_type ci3 = layout.add_cell ("C3"); + + db::Instance i1 = layout.cell (ci1).insert (db::CellInstArray (db::CellInst (ci2), db::Trans ())); + db::Instance i2 = layout.cell (ci2).insert (db::CellInstArray (db::CellInst (ci3), db::Trans ())); + + db::connected_clusters cc; + + db::ClusterInstance::inst_path_type p1; + p1.push_back (db::InstElement (i1)); + + db::ClusterInstance::inst_path_type p2; + p2.push_back (db::InstElement (i1)); + p2.push_back (db::InstElement (i2)); + + db::ClusterInstance::inst_path_type p3; + p3.push_back (db::InstElement (i2)); + + db::connected_clusters::connections_type x; + db::connected_clusters::connections_type::const_iterator ix; + + x = cc.connections_for_cluster (1); + EXPECT_EQ (x.size (), size_t (0)); + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (0)); + + cc.add_connection (1, db::ClusterInstance (1, p1)); + cc.add_connection (1, db::ClusterInstance (2, p2)); + + x = cc.connections_for_cluster (1); + EXPECT_EQ (x.size (), size_t (2)); + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (0)); + + cc.add_connection (2, db::ClusterInstance (1, p2)); + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (1)); + + cc.join_connected_clusters (1, 2); + x = cc.connections_for_cluster (1); + EXPECT_EQ (x.size (), size_t (3)); + ix = x.begin (); + EXPECT_EQ (ix->id (), size_t (1)); + EXPECT_EQ (ix->inst () == p1, true); + ++ix; + EXPECT_EQ (ix->id (), size_t (2)); + EXPECT_EQ (ix->inst () == p2, true); + ++ix; + EXPECT_EQ (ix->id (), size_t (1)); + EXPECT_EQ (ix->inst () == p2, true); + + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (0)); + + cc.add_connection (2, db::ClusterInstance (3, p1)); + + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, p1), 0), 2); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p1), 0), 0); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p2), 0), 1); + + cc.add_connection (17, db::ClusterInstance (2, p3)); + + // p3 == p2 minus 1 at the beginning + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p2), 1 /*one stripped*/), 17); +} From 1e4b2b414e6fbec83f34c2c374902938c8c53841 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Dec 2018 23:58:47 +0100 Subject: [PATCH 074/335] WIP: built test bench --- .../unit_tests/dbHierNetworkProcessorTests.cc | 153 +++++++++++++++++- src/db/unit_tests/dbHierProcessorTests.cc | 14 +- 2 files changed, 156 insertions(+), 11 deletions(-) diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index c3911ed38..c00758efb 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -29,6 +29,8 @@ #include "dbPath.h" #include "dbText.h" #include "dbLayout.h" +#include "dbStream.h" +#include "dbCommonReader.h" static std::string l2s (db::Connectivity::layer_iterator b, db::Connectivity::layer_iterator e) { @@ -383,12 +385,155 @@ TEST(30_LocalConnectedClusters) cc.add_connection (2, db::ClusterInstance (3, p1)); - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, p1), 0), 2); - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p1), 0), 0); - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p2), 0), 1); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, p1), 0), size_t (2)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p1), 0), size_t (0)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p2), 0), size_t (1)); cc.add_connection (17, db::ClusterInstance (2, p3)); // p3 == p2 minus 1 at the beginning - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p2), 1 /*one stripped*/), 17); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p2), 1 /*one stripped*/), size_t (17)); +} + +static void normalize_layer (db::Layout &layout, unsigned int layer) +{ + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + db::Shapes s (layout.is_editable ()); + s.swap (c->shapes (layer)); + for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); !i.at_end (); ++i) { + db::Polygon poly; + i->polygon (poly); + c->shapes (layer).insert (db::PolygonRef (poly, layout.shape_repository ())); + } + } +} + +static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const db::hier_clusters &hc, const db::local_cluster &cluster, const db::ICplxTrans &trans, const db::Connectivity &conn) +{ + // use property #1 to code the cell name + + db::PropertiesRepository &pr = out.layout ()->properties_repository (); + db::property_names_id_type pn_id = pr.prop_name_id (tl::Variant (1)); + db::PropertiesRepository::properties_set pm; + pm.insert (std::make_pair (pn_id, tl::Variant (out.layout ()->cell_name (ci)))); + db::properties_id_type cell_pid = pr.properties_id (pm); + + // copy the shapes from this cell + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + for (db::local_cluster::shape_iterator s = cluster.begin (*l); ! s.at_end (); ++s) { + db::Polygon poly = s->obj ().transformed (trans * db::ICplxTrans (s->trans ())); + out.insert (db::PolygonWithProperties (poly, cell_pid)); + } + } + + out.layout ()->cell_name (ci); + + // copy the shapes from the child cells too + typedef db::connected_clusters::connections_type connections_type; + const connections_type &connections = hc.clusters_per_cell (ci).connections_for_cluster (cluster.id ()); + for (connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + + db::ICplxTrans t = trans; + for (db::ClusterInstance::inst_path_type::const_iterator p = i->inst ().begin (); p != i->inst ().end (); ++p) { + t = t * p->complex_trans (); + } + + db::cell_index_type cci = i->inst ().back ().inst_ptr.cell_index (); + copy_cluster_shapes (out, cci, hc, hc.clusters_per_cell (cci).cluster_by_id (i->id ()), t, conn); + + } +} + +static void run_hc_test (tl::TestBase *_this, const std::string &file, const std::string &au_file) +{ + db::Layout ly; + unsigned int l1 = 0, l2 = 0, l3 = 0; + + { + db::LayerProperties p; + db::LayerMap lmap; + + p.layer = 1; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l1 = ly.insert_layer ()); + ly.set_properties (l1, p); + + p.layer = 2; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l2 = ly.insert_layer ()); + ly.set_properties (l2, p); + + p.layer = 3; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l3 = ly.insert_layer ()); + ly.set_properties (l3, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/"; + fn += file; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + normalize_layer (ly, l1); + normalize_layer (ly, l2); + normalize_layer (ly, l3); + + // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 + db::Connectivity conn; + conn.connect (l1, l1); + conn.connect (l2, l2); + conn.connect (l3, l3); + conn.connect (l1, l2); + conn.connect (l1, l3); + + db::hier_clusters hc; + hc.build (ly, ly.cell (*ly.begin_top_down ()), db::ShapeIterator::Polygons, conn); + + std::vector > net_layers; + + for (db::Layout::top_down_const_iterator td = ly.begin_top_down (); td != ly.end_top_down (); ++td) { + + const db::connected_clusters &clusters = hc.clusters_per_cell (*td); + for (db::connected_clusters::const_iterator c = clusters.begin (); c.at_end (); ++c) { + + net_layers.push_back (std::make_pair (0, ly.insert_layer ())); + + unsigned int lout = net_layers.back ().second; + + db::Shapes &out = ly.cell (*ly.begin_top_down ()).shapes (lout); + copy_cluster_shapes (out, *ly.begin_top_down (), hc, *c, db::ICplxTrans (), conn); + + db::Polygon::area_type area = 0; + for (db::Shapes::shape_iterator s = out.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + area += s->area (); + } + net_layers.back ().first = area; + + } + + } + + // sort layers by area so we have a consistent numbering + std::sort (net_layers.begin (), net_layers.end ()); + std::reverse (net_layers.begin (), net_layers.end ()); + + int ln = 1000; + for (std::vector >::const_iterator l = net_layers.begin (); l != net_layers.end (); ++l) { + ly.set_properties (l->second, db::LayerProperties (ln, 0)); + ++ln; + } + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/" + au_file); +} + +TEST(40_HierClusters) +{ + run_hc_test (_this, "hc_test_l1.gds", "hc_test_au1.gds"); } diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 6389d8ff0..541b23f00 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -126,7 +126,7 @@ private: * @brief Turns a layer into polygons and polygon references * The hierarchical processor needs polygon references and can't work on polygons directly. */ -void normalize_layer (db::Layout &layout, unsigned int layer) +static void normalize_layer (db::Layout &layout, unsigned int layer) { for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { db::Shapes s (layout.is_editable ()); @@ -140,7 +140,7 @@ void normalize_layer (db::Layout &layout, unsigned int layer) } -std::string contexts_to_s (db::Layout *layout, db::LocalProcessorContexts &contexts) +static std::string contexts_to_s (db::Layout *layout, db::LocalProcessorContexts &contexts) { std::string res; @@ -158,7 +158,7 @@ std::string contexts_to_s (db::Layout *layout, db::LocalProcessorContexts &conte return res; } -void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc, bool single, db::Coord dist, unsigned int nthreads = 0) +static void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc, bool single, db::Coord dist, unsigned int nthreads = 0) { db::Layout layout_org; @@ -257,22 +257,22 @@ void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, in db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); } -void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) +static void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) { run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true, 0, nthreads); } -void run_test_bool2 (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) +static void run_test_bool2 (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) { run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false, 0, nthreads); } -void run_test_bool_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) +static void run_test_bool_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) { run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true, dist, nthreads); } -void run_test_bool2_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) +static void run_test_bool2_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) { run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false, dist, nthreads); } From e8c86834cb0b2b1989698f06182cc28848abe65f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Dec 2018 00:17:04 +0100 Subject: [PATCH 075/335] WIP: hier network - a first testcase. --- src/db/unit_tests/dbHierNetworkProcessorTests.cc | 2 +- testdata/algo/hc_test_au1.gds | Bin 0 -> 1526 bytes testdata/algo/hc_test_l1.gds | Bin 0 -> 746 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 testdata/algo/hc_test_au1.gds create mode 100644 testdata/algo/hc_test_l1.gds diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index c00758efb..f3f45eae5 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -500,7 +500,7 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std for (db::Layout::top_down_const_iterator td = ly.begin_top_down (); td != ly.end_top_down (); ++td) { const db::connected_clusters &clusters = hc.clusters_per_cell (*td); - for (db::connected_clusters::const_iterator c = clusters.begin (); c.at_end (); ++c) { + for (db::connected_clusters::const_iterator c = clusters.begin (); ! c.at_end (); ++c) { net_layers.push_back (std::make_pair (0, ly.insert_layer ())); diff --git a/testdata/algo/hc_test_au1.gds b/testdata/algo/hc_test_au1.gds new file mode 100644 index 0000000000000000000000000000000000000000..21209e2b382763511db4ff4ccaac12374951ca93 GIT binary patch literal 1526 zcmb7@F-rqM5QSg%E}K)ML`Zpsg`E*g5kycd6a!kM5GxCRf~7@T|AAl%I~z+2OAE2` z2Uu8$RRjwmiAD{Mvq^^AfGcBhe8)T9yV;ppSQxX5G-nD=C?Wzdj(PtbiLuqqHJ}{j zS5}V~w{Fj0m)5Tq&QAAgNb`UAtgUVD0C57?NrN2hl;QkJDI)4U$^+yo{v*00;^}~& z8bsekB5DB943IAiMRyGQJBLKAl*qAnMAYqO6up&({qJ||uM;_T?t!uWjH0*dBYu;e zpKEP$Ck_UTtL(qwgAOS=(?7rGCu(mHeZDwy@7hEk*Fw=9Lw=x;@b_6qfai~ino#sU z{r^W<{X}oeuJ?|v?tZU_6um#2puc_L=Ee0hlM~nfjH2^g1Ad34HfCd(---wLub~b>P7p$$H zEN|akysd0rFP)$5r$~Z-JZsaPUBHh3J7eTtJ8R_rcGDwAPkQ9RcJ*lP2!F1|XP^9u zN%jG69tc`W%^mCg!4a7yq~qwA91crro+b7E$G!MH(sA$rOdXWeJnQ}OP1(QH`XZA! z9MkWLe=ChnsJXD(f5j*BE%NKtQD&EupEpX)9qW7`jO72KBOv?7bgI<6VD){kte<>e zb+dPLbr-XqQ1fCpwSIo-_9gWTqf Date: Mon, 3 Dec 2018 22:05:54 +0100 Subject: [PATCH 076/335] WIP: some refactoring, joining of clusters by child clusters. Needs testing. --- src/db/db/dbHierNetworkProcessor.cc | 259 +++++++++++++----- src/db/db/dbHierNetworkProcessor.h | 61 +++-- .../unit_tests/dbHierNetworkProcessorTests.cc | 68 +++-- 3 files changed, 273 insertions(+), 115 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 53a1870e8..3ee81d50c 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -31,6 +31,8 @@ #include #include +#include +#include namespace db { @@ -304,7 +306,7 @@ template class DB_PUBLIC local_cluster; template local_clusters::local_clusters () - : m_needs_update (false) + : m_needs_update (false), m_next_dummy_id (0) { // .. nothing yet .. } @@ -315,22 +317,38 @@ void local_clusters::clear () m_needs_update = false; m_clusters.clear (); m_bbox = box_type (); + m_next_dummy_id = 0; } template const local_cluster & local_clusters::cluster_by_id (typename local_cluster::id_type id) const { - // by convention the ID is the index + 1 so 0 can be used as "nil" - tl_assert (id > 0 && id <= m_clusters.size ()); - return m_clusters.objects ().item (id - 1); + tl_assert (id > 0); + + if (id > m_clusters.size ()) { + + // dummy connectors are not real ones - they just carry an arbitrary + // ID. Still they need to be treated as empty ones. + static local_cluster empty_cluster; + return empty_cluster; + + } else { + + // by convention the ID is the index + 1 so 0 can be used as "nil" + return m_clusters.objects ().item (id - 1); + + } } template void local_clusters::remove_cluster (typename local_cluster::id_type id) { - tl_assert (id > 0 && id <= m_clusters.size ()); + if (id == 0 || id > m_clusters.size ()) { + return; + } + // TODO: get rid of this const_cast by providing "delete by index" // m_clusters.erase (id - 1) local_cluster *to_delete = const_cast *> (& m_clusters.objects ().item (id - 1)); @@ -338,6 +356,25 @@ local_clusters::remove_cluster (typename local_cluster::id_type id) m_needs_update = true; } +template +void +local_clusters::join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id) +{ + tl_assert (id > 0); + if (with_id == 0 || with_id > m_clusters.size () || id > m_clusters.size ()) { + return; + } + + // TODO: this const_cast is required. But we know what we're doing ... + local_cluster *with = const_cast *> (& m_clusters.objects ().item (with_id - 1)); + local_cluster *first = const_cast *> (& m_clusters.objects ().item (id - 1)); + first->join_with (*with); + + m_clusters.erase (m_clusters.iterator_from_pointer (with)); + + m_needs_update = true; +} + template local_cluster * local_clusters::insert () @@ -488,48 +525,43 @@ void connected_clusters::add_connection (typename local_cluster::id_type id, const ClusterInstance &inst) { m_connections [id].push_back (inst); + m_rev_connections [inst] = id; } template void -connected_clusters::join_connected_clusters (typename local_cluster::id_type id, typename local_cluster::id_type with_id) +connected_clusters::join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id) { if (id == with_id) { return; } + // join the shape clusters + local_clusters::join_cluster_with (id, with_id); + + // handle the connections by translating + const connections_type &to_join = connections_for_cluster (with_id); connections_type &target = m_connections [id]; target.insert (target.end (), to_join.begin (), to_join.end ()); + + for (connections_type::const_iterator c = to_join.begin (); c != to_join.end (); ++c) { + m_rev_connections [*c] = id; + } + m_connections.erase (with_id); } template typename local_cluster::id_type -connected_clusters::find_cluster_with_connection (const ClusterInstance &ci, size_t strip) const +connected_clusters::find_cluster_with_connection (const ClusterInstance &inst) const { - for (typename std::map::id_type, connections_type>::const_iterator i = m_connections.begin (); i != m_connections.end (); ++i) { - - for (connections_type::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { - - const ClusterInstance::inst_path_type &path = j->inst (); - if (j->id () == ci.id () && path.size () == ci.inst ().size () - strip) { - - bool match = true; - for (size_t k = 0; k < path.size () && match; ++k) { - match = (path [k] == ci.inst () [k + strip]); - } - if (match) { - return i->first; - } - - } - - } - + typename std::map::id_type>::const_iterator rc = m_rev_connections.find (inst); + if (rc != m_rev_connections.end ()) { + return rc->second; + } else { + return 0; } - - return 0; } // explicit instantiations @@ -623,7 +655,7 @@ struct hc_receiver public: typedef typename hier_clusters::box_type box_type; typedef typename local_cluster::id_type id_type; - typedef std::map *> connector_map; + typedef std::map connector_map; /** * @brief Constructor @@ -659,6 +691,30 @@ public: return false; } + /** + * @brief Finally join the clusters in the join set + * + * This step is postponed because doing this while the iteration happens would + * invalidate the box trees. + */ + void join_superclusters () + { + for (typename std::list >::const_iterator sc = m_cm2join_sets.begin (); sc != m_cm2join_sets.end (); ++sc) { + + if (sc->empty ()) { + // dropped ones are empty + continue; + } + + typename std::set::const_iterator c = sc->begin (); + ++c; + for (typename std::set::const_iterator cc = c; cc != sc->end (); ++cc) { + mp_cell_clusters->join_cluster_with (*c, *cc); + } + + } + } + private: const db::Layout *mp_layout; db::connected_clusters *mp_cell_clusters; @@ -666,7 +722,8 @@ private: const cell_clusters_box_converter *mp_cbc; const db::Connectivity *mp_conn; connector_map m_connectors; - mutable std::map m_reduction_cache; + std::map *> m_cm2join_map; + std::list > m_cm2join_sets; /** * @brief Handles the cluster interactions between two instances or instance arrays @@ -701,7 +758,7 @@ private: box_type ib1 = bb1.transformed (tt1); box_type ib12 = ib1.transformed (t12); - ClusterInstance::inst_path_type pp1; + std::vector pp1; pp1.reserve (p1.size () + 1); pp1.insert (pp1.end (), p1.begin (), p1.end ()); pp1.push_back (db::InstElement (i1, ii1)); @@ -713,7 +770,7 @@ private: if (ib1.touches (ib2)) { - ClusterInstance::inst_path_type pp2; + std::vector pp2; pp2.reserve (p2.size () + 1); pp2.insert (pp2.end (), p2.begin (), p2.end ()); pp2.push_back (db::InstElement (i2, ii2)); @@ -767,11 +824,8 @@ private: if (i->interacts (*j, t21, *mp_conn)) { - ClusterInstance k1 (i->id (), p1); - reduce (k1); - - ClusterInstance k2 (j->id (), p2); - reduce (k2); + ClusterInstance k1 = make_path (i->id (), p1); + ClusterInstance k2 = make_path (j->id (), p2); typename connector_map::iterator x1 = m_connectors.find (k1); typename connector_map::iterator x2 = m_connectors.find (k2); @@ -780,23 +834,23 @@ private: if (x2 == m_connectors.end ()) { - db::local_cluster *connector = mp_cell_clusters->insert (); + id_type connector = mp_cell_clusters->insert_dummy (); m_connectors [k1] = connector; - mp_cell_clusters->add_connection (connector->id (), k1); - mp_cell_clusters->add_connection (connector->id (), k2); + mp_cell_clusters->add_connection (connector, k1); + mp_cell_clusters->add_connection (connector, k2); } else { - mp_cell_clusters->add_connection (x2->second->id (), k1); + mp_cell_clusters->add_connection (x2->second, k1); } } else if (x2 == m_connectors.end ()) { - mp_cell_clusters->add_connection (x1->second->id (), k2); + mp_cell_clusters->add_connection (x1->second, k2); } else if (x1->second != x2->second) { - mp_cell_clusters->join_connected_clusters (x1->second->id (), x2->second->id ()); - mp_cell_clusters->remove_cluster (x2->second->id ()); + mp_cell_clusters->join_cluster_with (x1->second, x2->second); + mp_cell_clusters->remove_cluster (x2->second); x2->second = x1->second; } @@ -836,7 +890,7 @@ private: if (b1.touches (ib2)) { - ClusterInstance::inst_path_type pp2; + std::vector pp2; pp2.reserve (p2.size () + 1); pp2.insert (pp2.end (), p2.begin (), p2.end ()); pp2.push_back (db::InstElement (i2, ii2)); @@ -870,10 +924,19 @@ private: if (c1.interacts (*j, t2, *mp_conn)) { - ClusterInstance k2 (j->id (), p2); - reduce (k2); + ClusterInstance k2 = make_path (j->id (), p2); - mp_cell_clusters->add_connection (c1.id (), k2); + id_type other = mp_cell_clusters->find_cluster_with_connection (k2); + if (other > 0) { + + // we found a child cluster that connects two clusters on our own level: + // we must join them into one, but not now. We're still iterating and + // would invalidate the box trees. So keep this now and combine the clusters later. + mark_to_join (other, c1.id ()); + + } else { + mp_cell_clusters->add_connection (c1.id (), k2); + } } @@ -881,35 +944,85 @@ private: } /** - * @brief Reduce the connection path to the connector highest in the hierarchy - * - * This feature shall prevent a situation where a connection is made to a sub-cluster of - * another hierarchical cluster. We always attach to the highest possible connector. + * @brief Inserts a pair of clusters to join */ - void reduce (ClusterInstance &k) const + void mark_to_join (id_type a, id_type b) { - std::map::const_iterator kr = m_reduction_cache.find (k); - if (kr != m_reduction_cache.end ()) { - k = kr->second; - return; + typename std::map *>::const_iterator x = m_cm2join_map.find (a); + typename std::map *>::const_iterator y = m_cm2join_map.find (b); + + if (x == m_cm2join_map.end ()) { + + if (y == m_cm2join_map.end ()) { + + m_cm2join_sets.push_back (std::set ()); + m_cm2join_sets.back ().insert (a); + m_cm2join_sets.back ().insert (b); + + m_cm2join_map [a] = &m_cm2join_sets.back (); + m_cm2join_map [b] = &m_cm2join_sets.back (); + + } else { + + y->second->insert (a); + m_cm2join_map [a] = y->second; + + } + + } else if (y == m_cm2join_map.end ()) { + + x->second->insert (b); + m_cm2join_map [b] = x->second; + + } else if (x->second != y->second) { + + // join two superclusters + x->second->insert (y->second->begin (), y->second->end ()); + for (typename std::set::const_iterator i = y->second->begin (); i != y->second->end (); ++i) { + m_cm2join_map [*i] = x->second; + } + y->second->clear (); + } + } - for (size_t rn = 1; rn + 1 < k.inst ().size (); ++rn) { + /** + * @brief Makes a valid path to a child cluster + * + * Cluster connections can only cross one level of hierarchy. This method + * creates necessary dummy entries for the given path. + */ + ClusterInstance make_path (id_type id, const std::vector &path) const + { + std::vector::const_iterator p = path.end (); + tl_assert (p != path.begin ()); - const db::connected_clusters &clusters = mp_tree->clusters_per_cell (k.inst () [rn - 1].inst_ptr.cell_index ()); - id_type red_id = clusters.find_cluster_with_connection (k, rn); - if (red_id > 0) { + while (true) { - // reduction possible - ClusterInstance::inst_path_type new_path; - new_path.insert (new_path.end (), k.inst ().begin (), k.inst ().begin () + rn); - k = ClusterInstance (red_id, new_path); - return; + --p; + + ClusterInstance ci (id, *p); + if (p == path.begin ()) { + return ci; + } + + connected_clusters &target_cc = mp_tree->clusters_per_cell (p [-1].inst_ptr.cell_index ()); + id_type parent_cluster = target_cc.find_cluster_with_connection (ci); + + if (parent_cluster > 0) { + + // taken parent + id = parent_cluster; + + } else { + + // no parent -> create vertical connector + id = target_cc.insert_dummy (); + target_cc.add_connection (id, ci); } } - } }; @@ -987,6 +1100,9 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou bs2.process (rec, 1 /*touching*/, local_cluster_box_convert (), cibc); } + + // finally join local clusters which got connected by child clusters + rec.join_superclusters (); } template @@ -1002,6 +1118,15 @@ hier_clusters::clusters_per_cell (db::cell_index_type cell_index) const } } +template +connected_clusters & +hier_clusters::clusters_per_cell (db::cell_index_type cell_index) +{ + typename std::map >::iterator c = m_per_cell_clusters.find (cell_index); + tl_assert (c != m_per_cell_clusters.end ()); + return c->second; +} + // explicit instantiations template class DB_PUBLIC hier_clusters; diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 3964cc76b..f15013901 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -254,6 +254,13 @@ public: */ void remove_cluster (typename local_cluster::id_type id); + /** + * @brief Joins a cluster with another one + * + * This will also remove the other cluster. + */ + void join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id); + /** * @brief Gets the bounding box of the clusters */ @@ -304,12 +311,23 @@ public: */ local_cluster *insert (); + /** + * @brief Allocates a new ID for dummy clusters + * + * Dummy cluster ID's will deliver empty clusters. Used for connectors. + */ + typename local_cluster::id_type insert_dummy () + { + return --m_next_dummy_id; + } + private: void ensure_sorted (); bool m_needs_update; box_type m_bbox; tree_type m_clusters; + size_t m_next_dummy_id; }; /** @@ -318,10 +336,8 @@ private: class DB_PUBLIC ClusterInstance { public: - typedef std::vector inst_path_type; - - ClusterInstance (size_t id, const inst_path_type &inst_path) - : m_id (id), m_inst_path (inst_path) + ClusterInstance (size_t id, const db::InstElement &inst) + : m_id (id), m_inst (inst) { // .. nothing yet .. } @@ -337,9 +353,9 @@ public: /** * @brief Gets the instance path */ - const inst_path_type &inst () const + const db::InstElement &inst () const { - return m_inst_path; + return m_inst; } /** @@ -347,7 +363,7 @@ public: */ bool operator== (const ClusterInstance &other) const { - return m_id == other.m_id && m_inst_path == other.m_inst_path; + return m_id == other.m_id && m_inst == other.m_inst; } /** @@ -358,12 +374,12 @@ public: if (m_id != other.m_id) { return m_id < other.m_id; } - return m_inst_path < other.m_inst_path; + return m_inst < other.m_inst; } private: size_t m_id; - inst_path_type m_inst_path; + db::InstElement m_inst; }; template class hier_clusters; @@ -393,27 +409,29 @@ public: */ const connections_type &connections_for_cluster (typename local_cluster::id_type id) const; + /** + * @brief Reverse "connections_for_cluster" + * Finds the cluster which has a connection given by inst. + * Returns 0 if the given connection does not exist. + */ + typename local_cluster::id_type find_cluster_with_connection (const ClusterInstance &inst) const; + /** * @brief Adds a connection between a local cluster and one from a child instance */ void add_connection (typename local_cluster::id_type, const ClusterInstance &inst); /** - * @brief Joins all connections of with_id to id - */ - void join_connected_clusters (typename local_cluster::id_type id, typename local_cluster::id_type with_id); - - /** - * @brief Reverse "connections_for_cluster" + * @brief Joins the cluster id with the cluster with_id * - * Finds the cluster which has a connection given by inst. "strip" elements - * are stipped from the front of the instantiation path. - * This method returns 0 if no cluster can be found. + * The "with_id" cluster is removed. All connections of "with_id" are transferred to the + * first one. All shapes of "with_id" are transferred to "id". */ - typename local_cluster::id_type find_cluster_with_connection (const ClusterInstance &inst, size_t strip) const; + void join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id); private: std::map::id_type, connections_type> m_connections; + std::map::id_type> m_rev_connections; }; template class cell_clusters_box_converter; @@ -444,6 +462,11 @@ public: */ const connected_clusters &clusters_per_cell (db::cell_index_type cell_index) const; + /** + * @brief Gets the connected clusters for a given cell (non-const version) + */ + connected_clusters &clusters_per_cell (db::cell_index_type cell_index); + /** * @brief Clears this collection */ diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index f3f45eae5..b28c7fee5 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -337,16 +337,6 @@ TEST(30_LocalConnectedClusters) db::connected_clusters cc; - db::ClusterInstance::inst_path_type p1; - p1.push_back (db::InstElement (i1)); - - db::ClusterInstance::inst_path_type p2; - p2.push_back (db::InstElement (i1)); - p2.push_back (db::InstElement (i2)); - - db::ClusterInstance::inst_path_type p3; - p3.push_back (db::InstElement (i2)); - db::connected_clusters::connections_type x; db::connected_clusters::connections_type::const_iterator ix; @@ -355,44 +345,67 @@ TEST(30_LocalConnectedClusters) x = cc.connections_for_cluster (2); EXPECT_EQ (x.size (), size_t (0)); - cc.add_connection (1, db::ClusterInstance (1, p1)); - cc.add_connection (1, db::ClusterInstance (2, p2)); + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + cc.add_connection (1, db::ClusterInstance (1, db::InstElement (i1))); + cc.add_connection (1, db::ClusterInstance (2, db::InstElement (i2))); x = cc.connections_for_cluster (1); EXPECT_EQ (x.size (), size_t (2)); x = cc.connections_for_cluster (2); EXPECT_EQ (x.size (), size_t (0)); - cc.add_connection (2, db::ClusterInstance (1, p2)); + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + // [#2] -> i2:#1 + cc.add_connection (2, db::ClusterInstance (1, db::InstElement (i2))); x = cc.connections_for_cluster (2); EXPECT_EQ (x.size (), size_t (1)); - cc.join_connected_clusters (1, 2); + cc.join_cluster_with (1, 2); x = cc.connections_for_cluster (1); EXPECT_EQ (x.size (), size_t (3)); ix = x.begin (); EXPECT_EQ (ix->id (), size_t (1)); - EXPECT_EQ (ix->inst () == p1, true); + EXPECT_EQ (ix->inst () == db::InstElement (i1), true); ++ix; EXPECT_EQ (ix->id (), size_t (2)); - EXPECT_EQ (ix->inst () == p2, true); + EXPECT_EQ (ix->inst () == db::InstElement (i2), true); ++ix; EXPECT_EQ (ix->id (), size_t (1)); - EXPECT_EQ (ix->inst () == p2, true); + EXPECT_EQ (ix->inst () == db::InstElement (i2), true); x = cc.connections_for_cluster (2); EXPECT_EQ (x.size (), size_t (0)); - cc.add_connection (2, db::ClusterInstance (3, p1)); + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + // [#2] -> i2:#1 + // -> i1:#3 + cc.add_connection (2, db::ClusterInstance (3, db::InstElement (i1))); - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, p1), 0), size_t (2)); - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p1), 0), size_t (0)); - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p2), 0), size_t (1)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, db::InstElement (i1))), size_t (2)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i1))), size_t (0)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i2))), size_t (1)); - cc.add_connection (17, db::ClusterInstance (2, p3)); + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + // -> i2:#1 + // -> i1:#3 + cc.join_cluster_with (1, 2); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, db::InstElement (i1))), size_t (1)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (1, db::InstElement (i1))), size_t (1)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i1))), size_t (0)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i2))), size_t (1)); - // p3 == p2 minus 1 at the beginning - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p2), 1 /*one stripped*/), size_t (17)); + x = cc.connections_for_cluster (1); + EXPECT_EQ (x.size (), size_t (4)); + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (0)); } static void normalize_layer (db::Layout &layout, unsigned int layer) @@ -433,12 +446,9 @@ static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const const connections_type &connections = hc.clusters_per_cell (ci).connections_for_cluster (cluster.id ()); for (connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { - db::ICplxTrans t = trans; - for (db::ClusterInstance::inst_path_type::const_iterator p = i->inst ().begin (); p != i->inst ().end (); ++p) { - t = t * p->complex_trans (); - } + db::ICplxTrans t = trans * i->inst ().complex_trans (); - db::cell_index_type cci = i->inst ().back ().inst_ptr.cell_index (); + db::cell_index_type cci = i->inst ().inst_ptr.cell_index (); copy_cluster_shapes (out, cci, hc, hc.clusters_per_cell (cci).cluster_by_id (i->id ()), t, conn); } From 42f153672ba40e939785def9537d1e97e40e160d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Dec 2018 23:02:01 +0100 Subject: [PATCH 077/335] WIP: proceed with debugging of hier network processor --- src/db/db/dbHierNetworkProcessor.cc | 35 ++++++++++++++---- .../unit_tests/dbHierNetworkProcessorTests.cc | 9 ++++- testdata/algo/hc_test_au2.gds | Bin 0 -> 3182 bytes testdata/algo/hc_test_l2.gds | Bin 0 -> 1224 bytes 4 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 testdata/algo/hc_test_au2.gds create mode 100644 testdata/algo/hc_test_l2.gds diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 3ee81d50c..5261a708d 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -349,10 +349,11 @@ local_clusters::remove_cluster (typename local_cluster::id_type id) return; } - // TODO: get rid of this const_cast by providing "delete by index" - // m_clusters.erase (id - 1) + // TODO: this const_cast is required. But we know what we're doing ... + // NOTE: we cannot really delete a cluster as this would shift the indexes. So + // we just clear them. local_cluster *to_delete = const_cast *> (& m_clusters.objects ().item (id - 1)); - m_clusters.erase (m_clusters.iterator_from_pointer (to_delete)); + to_delete->clear (); m_needs_update = true; } @@ -370,7 +371,9 @@ local_clusters::join_cluster_with (typename local_cluster::id_type id, typ local_cluster *first = const_cast *> (& m_clusters.objects ().item (id - 1)); first->join_with (*with); - m_clusters.erase (m_clusters.iterator_from_pointer (with)); + // NOTE: we cannot really delete a cluster as this would shift the indexes. So + // we just clear them. + with->clear (); m_needs_update = true; } @@ -707,8 +710,8 @@ public: } typename std::set::const_iterator c = sc->begin (); - ++c; - for (typename std::set::const_iterator cc = c; cc != sc->end (); ++cc) { + typename std::set::const_iterator cc = c; + for (++cc; cc != sc->end (); ++cc) { mp_cell_clusters->join_cluster_with (*c, *cc); } @@ -1074,12 +1077,28 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou hc_receiver rec (layout, local, *this, cbc, conn); cell_inst_clusters_box_converter cibc (cbc); + // The box scanner needs pointers so we have to first store the instances + // delivered by the cell's iterator (which does not deliver real pointer). + + std::vector inst_storage; + + // TODO: there should be a cell.size () for this ... + size_t n = 0; + for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { + n += 1; + } + + inst_storage.reserve (n); + for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { + inst_storage.push_back (*inst); + } + // handle instance to instance connections { db::box_scanner bs; - for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { + for (std::vector::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) { bs.insert (inst.operator-> (), 0); } @@ -1094,7 +1113,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou for (typename connected_clusters::const_iterator c = local.begin (); c != local.end (); ++c) { bs2.insert1 (c.operator-> (), 0); } - for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { + for (std::vector::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) { bs2.insert2 (inst.operator-> (), 0); } diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index b28c7fee5..c18d6291e 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -516,8 +516,8 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std unsigned int lout = net_layers.back ().second; - db::Shapes &out = ly.cell (*ly.begin_top_down ()).shapes (lout); - copy_cluster_shapes (out, *ly.begin_top_down (), hc, *c, db::ICplxTrans (), conn); + db::Shapes &out = ly.cell (*td).shapes (lout); + copy_cluster_shapes (out, *td, hc, *c, db::ICplxTrans (), conn); db::Polygon::area_type area = 0; for (db::Shapes::shape_iterator s = out.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { @@ -547,3 +547,8 @@ TEST(40_HierClusters) { run_hc_test (_this, "hc_test_l1.gds", "hc_test_au1.gds"); } + +TEST(41_HierClusters) +{ + run_hc_test (_this, "hc_test_l2.gds", "hc_test_au2.gds"); +} diff --git a/testdata/algo/hc_test_au2.gds b/testdata/algo/hc_test_au2.gds new file mode 100644 index 0000000000000000000000000000000000000000..86ba92af2e58005b024b12fee6646d1c2bc99925 GIT binary patch literal 3182 zcma);KWI~N5XUbs$;)ev5~ZL+1_y_N0TBvT5JAnLSkNJZh=YTpLkEWr4jsCZd~iczFBHGySSM6@oSR(z z{zu6d{0E~q691GcM_zOEk?$?UyXJVjPE@TkD^(79`o(SEe`EcHm*cu^#C4q3V%CiT&!_OZ#9v8PS8jQwe_Wv=xTwkq`B=^TY&>%l{` z?ocp7`1lbzh#OU!$1X5nBt`>1@c?RQ>Qf7F!8$1JSY-Q-uQJb&`R z)pyW&?O1tER4ZYMB~&@+`=6Pdqb75AP2TT(m+qBw2vy!)%h&&ni5bl>3uWra?0VF&ai|#x2Kz=fY6q)w*Ig9}F+Z{^0Jg!Lm~2LW?al zA1X1vLqY62G`X8nKDc2<7$J!|GZGg)(0F8i9?(XwyI$Iu^@zVnB;6f>>Cjw6si-!p6?RB47#& z!6rqD6kDbUsZyp$VIip&BO=+2Zl1?`XZA8P;AG(E8_sa}@8b*{JZ}j}&8r_GhL77A z!M)-;BF`;8od+iTaAxlP<7c~He^0eK4?ll;*+vpp`>1hX?icI_W#`z|0=0G zIRHjp=QO>yTJn2&|59sLCb66`SQP(`G&-c|!eajgKiPjmp8ZtH?E2*Cw$XHDl^>`_ z^8JAlknQ7U+h}^q;{ASJKlx``XRp+{(^(H`db*oRe}6;wCG`uVHJyJ>(`By}e%bdo znRg@O`4$i-q|tO?>aXM4lt#|ApTCecZb(kYHL>naO|}!?4d>*B^of&jcKC`|^Lw?Q zX0}*gDc?Wdk;ffUIrvyUrXP1o{gwQeExB%)RK`j6e;7@#&WAMeMLZue*53e)Kz2Q( z>B3_DrF>ns$fHfYKdW7Lt$ip>KiaJJ|B_rqlcZ92qxm$N9!-|~rF@;fBD+oT3H9Fd Pt(>NJo0(ts8wB_V&eMqF literal 0 HcmV?d00001 From 37fad6da979affd892d84c71e8e590ca76cf0d6c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 4 Dec 2018 22:30:18 +0100 Subject: [PATCH 078/335] WIP: hier network processor - some debugging, more tests. --- src/db/db/dbBoxScanner.h | 3 +- src/db/db/dbHierNetworkProcessor.cc | 36 +++++++++++++----- .../unit_tests/dbHierNetworkProcessorTests.cc | 10 +++++ testdata/algo/hc_test_au3.gds | Bin 0 -> 3324 bytes testdata/algo/hc_test_au4.gds | Bin 0 -> 3418 bytes testdata/algo/hc_test_l3.gds | Bin 0 -> 1258 bytes testdata/algo/hc_test_l4.gds | Bin 0 -> 982 bytes 7 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 testdata/algo/hc_test_au3.gds create mode 100644 testdata/algo/hc_test_au4.gds create mode 100644 testdata/algo/hc_test_l3.gds create mode 100644 testdata/algo/hc_test_l4.gds diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index 5c9372fe8..e8c46c693 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -621,8 +621,9 @@ public: // below m_scanner_thr elements use the brute force approach which is faster in that case for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) { + box_type b1 = bc1 (*i->first); for (iterator_type2 j = m_pp2.begin (); j != m_pp2.end (); ++j) { - if (bs_boxes_overlap (bc1 (*i->first), bc2 (*j->first), enl)) { + if (bs_boxes_overlap (b1, bc2 (*j->first), enl)) { rec.add (i->first, i->second, j->first, j->second); if (rec.stop ()) { return false; diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 5261a708d..f3db932aa 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -103,9 +103,21 @@ interaction_test (const db::PolygonRef &a, const db::PolygonRef &b, const Trans { // TODO: this could be part of db::interact (including transformation) if (a.obj ().is_box () && b.obj ().is_box ()) { - return db::interact (a.obj ().box ().transformed (b.trans ().inverted () * a.trans ()), b.obj ().box ().transformed (trans)); + return db::interact (a.obj ().box ().transformed (a.trans ()), b.obj ().box ().transformed (trans * Trans (b.trans ()))); } else { - return db::interact (a.obj ().transformed (b.trans ().inverted () * a.trans ()), b.obj ().transformed (trans)); + return db::interact (a.obj ().transformed (a.trans ()), b.obj ().transformed (trans * Trans (b.trans ()))); + } +} + +template +static bool +interaction_test (const db::PolygonRef &a, const db::PolygonRef &b, const db::unit_trans &) +{ + // TODO: this could be part of db::interact (including transformation) + if (a.obj ().is_box () && b.obj ().is_box ()) { + return db::interact (a.obj ().box ().transformed (a.trans ()), b.obj ().box ().transformed (b.trans ())); + } else { + return db::interact (a.obj ().transformed (a.trans ()), b.obj ().transformed (b.trans ())); } } @@ -265,12 +277,11 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans { const_cast *> (this)->ensure_sorted (); - if (! other.bbox ().touches (bbox ())) { + box_type common = other.bbox ().transformed (trans) & bbox (); + if (common.empty ()) { return false; } - box_type common = other.bbox () & bbox (); - db::box_scanner2 scanner; transformed_box bc_t (trans); db::box_convert bc; @@ -586,6 +597,11 @@ public: // .. nothing yet .. } + const box_type &operator() (const db::CellInst &cell_inst) const + { + return (*this) (cell_inst.cell_index ()); + } + const box_type &operator() (db::cell_index_type cell_index) const { typename std::map::const_iterator b = m_cache.find (cell_index); @@ -601,7 +617,7 @@ public: const db::Cell &cell = mp_layout->cell (cell_index); for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { const db::CellInstArray &inst_array = inst->cell_inst (); - box += inst_array.raw_bbox () * (*this) (inst_array.object ().cell_index ()); + box += inst_array.bbox (*this); } return m_cache.insert (std::make_pair (cell_index, box)).first->second; @@ -741,10 +757,10 @@ private: void add_pair (const db::Instance &i1, const std::vector &p1, const db::ICplxTrans &t1, const db::Instance &i2, const std::vector &p2, const db::ICplxTrans &t2) { box_type bb1 = (*mp_cbc) (i1.cell_index ()); - box_type b1 = (i1.cell_inst ().raw_bbox () * bb1).transformed (t1); + box_type b1 = i1.cell_inst ().bbox (*mp_cbc).transformed (t1); box_type bb2 = (*mp_cbc) (i2.cell_index ()); - box_type b2 = (i2.cell_inst ().raw_bbox () * bb2).transformed (t2); + box_type b2 = i2.cell_inst ().bbox (*mp_cbc).transformed (t2); if (! b1.touches (b2)) { return; @@ -878,7 +894,7 @@ private: box_type b1 = c1.bbox (); box_type bb2 = (*mp_cbc) (i2.cell_index ()); - box_type b2 = (i2.cell_inst ().raw_bbox () * bb2).transformed (t2); + box_type b2 = i2.cell_inst ().bbox (*mp_cbc).transformed (t2); if (! b1.touches (b2)) { return; @@ -1043,7 +1059,7 @@ struct cell_inst_clusters_box_converter box_type operator() (const db::Instance &inst) const { - return inst.cell_inst ().raw_bbox () * (*mp_cbc) (inst.cell_index ()); + return inst.cell_inst ().bbox (*mp_cbc); } private: diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index c18d6291e..3ca36d282 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -552,3 +552,13 @@ TEST(41_HierClusters) { run_hc_test (_this, "hc_test_l2.gds", "hc_test_au2.gds"); } + +TEST(42_HierClusters) +{ + run_hc_test (_this, "hc_test_l3.gds", "hc_test_au3.gds"); +} + +TEST(43_HierClusters) +{ + run_hc_test (_this, "hc_test_l4.gds", "hc_test_au4.gds"); +} diff --git a/testdata/algo/hc_test_au3.gds b/testdata/algo/hc_test_au3.gds new file mode 100644 index 0000000000000000000000000000000000000000..c1998d1e48d9d6630451916beead0e3003bd2a3e GIT binary patch literal 3324 zcma);KWG$D5XR^B_VzaM45vh}xWd9h@IaJ+3L>aiC&=EEQQ5S5z#qdG|8Df)jr&od8d%sfw2K1Wm;RjLja$8SGp-fA#| zhcB3Wy^N}FHHzbRze@ZJGq}ARU6p_#j)q)Y_4qc7dG^Of7fBjxO zbN4Ru{%=tBuFJglS*bc$6i-x*<@fU-k#wIg(;V@$A(Tva;Oo#QB) zd@v!`cUb)SG){O8&OG>*xUqM0^3J_B%e>iU277b)NA=CNfAjM3{T7^$8LTy&^D9;V z$N9W3hjWenmyUxeqLEdpIi>1Czy6$a_FK&Bi+I1Q9k&`I{vPDO}X1R{;Ea0mwjxXGmvyC&UR9zh0lnay8;aN;tuD888rRtz1m)Mlt*<{)f z%4A5x8yQs>n(D%x!SslU0n-ou990Kxxdv0>9oL)(VS>YC=cqbp$t5*>a^3<~rZEv^ zgtw%>8`hXkMiZdaG%NdcK`HA_^n`|7ZqlNabmE$J`(8<@I;~jNh^ZO-<2ba8vx@20 ZQFYnBqPo_7FEe_n%a!vo`8I|j{RKXRFYf>V literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au4.gds b/testdata/algo/hc_test_au4.gds new file mode 100644 index 0000000000000000000000000000000000000000..57d1aa7c0b610f89035a3f55f25b1bdc0a53555e GIT binary patch literal 3418 zcma);y-!q85XH}aJr-OMbWy|?BM}J+n}~^tnivz20wakQ6voQJKfuC*!h*s=I|~aE zDd@DIu&|)8upoxQ(t?757z%*(HD=x`o6 zMD2M1wT^;wjb;4;xmxzCb|H$LIN0A!JGWbL&P@Y_GeD`TRJ*Xb|HdQd)??>{jmOSy zZKYKEdfeQ9^CSHyofB>>0vZJ_!quq<2UwSH0b-b=KDD9#l2d_ zUNmT#`@*DUtg%7MTyK+>vG&chzGv%boMV%gai&dL#`!d8nR|VoRtx^Soy$=)e)|r` zw~hPxsC0NmHuLT$zm5Im{CyJj<~*Y&`-Ra~!k&?x zDW5>K3$d>bT(La=vXSxs9&$fk7KXhq=Yj11!iw637A?l`jum?zSelSM%AQH6cG3`6 zvQOC*r6tDlL^7!4dwaX5K`ZQMEF^n+!k_1iD^Tt129p-gQh&Y(Tj%B6O)2M3cy7@G zdK;mJy$!x+6*x!DK(%`s;sQF^ozers;v3*B^QBZfY0~Pw7|v7S&Ir&yn^Ml%CJzsCIIn7Ej({5q|&yd;SRk literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l3.gds b/testdata/algo/hc_test_l3.gds new file mode 100644 index 0000000000000000000000000000000000000000..f0b6251fa56bdf76b596c9720aec4087a8ad3a46 GIT binary patch literal 1258 zcmaKrJxml)6ot>u&b}FNB&$L<1%>4%goK2U7!!31j3iKOVT^?ZrG|XW`&^&k=#Zuh^Y733$=*D9{!=Nl>yc-ljixKh z{6G-O_xnmfwvSsaqv;9r_gAz0h+Kb_wBTFSv2VC+yb4{5qEpTE$ct0sB8tM{kb zb({JQrRm4JWquje2g$sVGI$NteQDE>rVC5&|0T@}XGo=PPaAABJ)9}{3k^JbNp>3I T6Kd{@y^N-J8mV6z;QP1+?#!F1 literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l4.gds b/testdata/algo/hc_test_l4.gds new file mode 100644 index 0000000000000000000000000000000000000000..933c5ecee9cdc5377ab54beb14398587c91f5fb1 GIT binary patch literal 982 zcmaKqtxv;H6va=w_N|OE1{r3lKp-ZT0E&A|}BjNSMH3kO(A# z5Cjr|KoArHv1A*JvFvi+>n`mQN|TpgbJ}y?{epsR?;x=3^ecQgm_inQ{2iXHDjQ`W zpK_O1ju$rXE*gt#*R$tmyHx~kvQH_sIuFMK)R+wmDr*BMO)f*k2>kY(D`bdl5x%)`t9c zR6pnX#+}#?=~UQ%%Y_aoIx}8>%um!>C+faya_?G1UpGS0HIw{6+T-stngCB9qgA2k zhVlDTQT{|9OS<=(I&ahK0Yx{nN%Xfa^}INLrgy6QA5nCkYl5HW{mlK&h5Wt({D4R( zI@9>m_;*V^rK(%oTt Date: Tue, 4 Dec 2018 23:21:19 +0100 Subject: [PATCH 079/335] WIP: more tests, some debugging. --- src/db/db/dbHierNetworkProcessor.h | 70 ++++++++++++++++++ .../unit_tests/dbHierNetworkProcessorTests.cc | 31 +++++--- testdata/algo/hc_test_au5.gds | Bin 0 -> 2516 bytes testdata/algo/hc_test_au6.gds | Bin 0 -> 3812 bytes testdata/algo/hc_test_l5.gds | Bin 0 -> 918 bytes testdata/algo/hc_test_l6.gds | Bin 0 -> 924 bytes 6 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 testdata/algo/hc_test_au5.gds create mode 100644 testdata/algo/hc_test_au6.gds create mode 100644 testdata/algo/hc_test_l5.gds create mode 100644 testdata/algo/hc_test_l6.gds diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index f15013901..69918db97 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -383,6 +383,48 @@ private: }; template class hier_clusters; +template class connected_clusters; + +/** + * @brief An iterator delivering all clusters of a connected_clusters set + */ +template +class DB_PUBLIC connected_clusters_iterator +{ +public: + typedef typename local_cluster::id_type value_type; + + connected_clusters_iterator (const connected_clusters &c); + + connected_clusters_iterator &operator++ () + { + if (! m_lc_iter.at_end ()) { + ++m_lc_iter; + } else if (m_x_iter != m_x_iter_end) { + ++m_x_iter; + } + return *this; + } + + bool at_end () const + { + return m_lc_iter.at_end () && m_x_iter == m_x_iter_end; + } + + value_type operator* () const + { + if (m_lc_iter.at_end ()) { + return m_x_iter->first; + } else { + return m_lc_iter->id (); + } + } + +private: + typename local_clusters::const_iterator m_lc_iter; + typedef std::list connections_type; + typename std::map::id_type, connections_type>::const_iterator m_x_iter, m_x_iter_end; +}; /** * @brief Local clusters with connections to clusters from child cells @@ -394,6 +436,7 @@ class DB_PUBLIC connected_clusters public: typedef std::list connections_type; typedef typename local_clusters::box_type box_type; + typedef connected_clusters_iterator all_iterator; /** * @brief Constructor @@ -429,11 +472,38 @@ public: */ void join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id); + /** + * @brief An iterator delivering all clusters (even the connectors) + * + * This iterator will deliver ID's rather than cluster objects. + */ + all_iterator begin_all () const + { + return connected_clusters_iterator (*this); + } + private: + template friend class connected_clusters_iterator; + std::map::id_type, connections_type> m_connections; std::map::id_type> m_rev_connections; }; +template +connected_clusters_iterator::connected_clusters_iterator (const connected_clusters &c) + : m_lc_iter (c.begin ()) +{ + size_t max_id = 0; + for (typename connected_clusters::const_iterator i = c.begin (); i != c.end (); ++i) { + if (i->id () > max_id) { + max_id = i->id (); + } + } + + m_x_iter = c.m_connections.lower_bound (max_id + 1); + m_x_iter_end = c.m_connections.end (); +} + template class cell_clusters_box_converter; /** diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 3ca36d282..e92130442 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -421,7 +421,7 @@ static void normalize_layer (db::Layout &layout, unsigned int layer) } } -static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const db::hier_clusters &hc, const db::local_cluster &cluster, const db::ICplxTrans &trans, const db::Connectivity &conn) +static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const db::hier_clusters &hc, db::local_cluster::id_type cluster_id, const db::ICplxTrans &trans, const db::Connectivity &conn) { // use property #1 to code the cell name @@ -431,9 +431,12 @@ static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const pm.insert (std::make_pair (pn_id, tl::Variant (out.layout ()->cell_name (ci)))); db::properties_id_type cell_pid = pr.properties_id (pm); + const db::connected_clusters &clusters = hc.clusters_per_cell (ci); + const db::local_cluster &lc = clusters.cluster_by_id (cluster_id); + // copy the shapes from this cell for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { - for (db::local_cluster::shape_iterator s = cluster.begin (*l); ! s.at_end (); ++s) { + for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { db::Polygon poly = s->obj ().transformed (trans * db::ICplxTrans (s->trans ())); out.insert (db::PolygonWithProperties (poly, cell_pid)); } @@ -443,13 +446,13 @@ static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const // copy the shapes from the child cells too typedef db::connected_clusters::connections_type connections_type; - const connections_type &connections = hc.clusters_per_cell (ci).connections_for_cluster (cluster.id ()); + const connections_type &connections = clusters.connections_for_cluster (cluster_id); for (connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { db::ICplxTrans t = trans * i->inst ().complex_trans (); db::cell_index_type cci = i->inst ().inst_ptr.cell_index (); - copy_cluster_shapes (out, cci, hc, hc.clusters_per_cell (cci).cluster_by_id (i->id ()), t, conn); + copy_cluster_shapes (out, cci, hc, i->id (), t, conn); } } @@ -510,7 +513,7 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std for (db::Layout::top_down_const_iterator td = ly.begin_top_down (); td != ly.end_top_down (); ++td) { const db::connected_clusters &clusters = hc.clusters_per_cell (*td); - for (db::connected_clusters::const_iterator c = clusters.begin (); ! c.at_end (); ++c) { + for (db::connected_clusters::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { net_layers.push_back (std::make_pair (0, ly.insert_layer ())); @@ -543,22 +546,32 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/" + au_file); } -TEST(40_HierClusters) +TEST(41_HierClusters) { run_hc_test (_this, "hc_test_l1.gds", "hc_test_au1.gds"); } -TEST(41_HierClusters) +TEST(42_HierClusters) { run_hc_test (_this, "hc_test_l2.gds", "hc_test_au2.gds"); } -TEST(42_HierClusters) +TEST(43_HierClusters) { run_hc_test (_this, "hc_test_l3.gds", "hc_test_au3.gds"); } -TEST(43_HierClusters) +TEST(44_HierClusters) { run_hc_test (_this, "hc_test_l4.gds", "hc_test_au4.gds"); } + +TEST(45_HierClusters) +{ + run_hc_test (_this, "hc_test_l5.gds", "hc_test_au5.gds"); +} + +TEST(46_HierClusters) +{ + run_hc_test (_this, "hc_test_l6.gds", "hc_test_au6.gds"); +} diff --git a/testdata/algo/hc_test_au5.gds b/testdata/algo/hc_test_au5.gds new file mode 100644 index 0000000000000000000000000000000000000000..9ca93f4542fa854fcd84ca99954720519f60b7f4 GIT binary patch literal 2516 zcma);v1=4j5XQgT+uK{?s)?Ra<027)jR!^$L=eGf5iy{}6~W5FKf%%>rRh@yOpz`r zQdn4|v=FgKX<=az#Nrgez+G}@a%P;_-SNF}u1w(gg?X7T@4cD%-XTH|%pjfJU`;=kDUohws;RZr$I!w)*Nx3u&!9FN&gj*Ad1*bRs|n(a8Wn zbF@k{*wznFY5E_H3lWat-}$scbeIs)3Q(N{Y7M2vh2{CX&xtxIk#O*WsNZ+gcqc8- z?`?7Z0+Dd{6L9RQqsBW6NAd%Ae%@=FZ{k_baE|le@j(k}oavw6%O~p267ByG^4)cb ze!W#{Tv(P5)MH+s5&~R(9B(N#p3(pRRkwelz1wp4LfLnA*9A46)uuGRyCL=B{WIfb zxqnBEb6rdFx!#}ozDIK&yaAGwNU3qAmtXzn@88&e(bcSPn^_;bUOcL0>_vl?xi3sw z#u^*6%=I>D8Eapr_1&+dagI$|#+f#08RyfWW$yJMtvdee&T$mA9zEvq9btbyEghbS zGar7;+&G(@zbny1h5af?Yco7+*$@9MFu~5#pZ2HBcX?oBhu4>K?!N?1t+J~W)Hu_h z(zdIvIX^#n+$_%H&8erBd#2PyKEKy}>((5=V_QOy%KcoKb<{X>NQ-Aa4DknjIVT_h literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au6.gds b/testdata/algo/hc_test_au6.gds new file mode 100644 index 0000000000000000000000000000000000000000..520e0bbfd79758d90de4c42fa164d2610c591f16 GIT binary patch literal 3812 zcma);Jxml)5XXnRz2$@hK}jU!3JMAmNz{ZyOo&0G&`6>M1)-v#u%NK8ps=8@F`=-a zFp+{z3JVJg3JVHiC@d^2Cjj`6#4RYLN0( zsp^lEqkzuQS!#*@o3g2}|7I^yQ#y0?+M~<2UOg*b>6^PS{d8!6@|omwVHjS&L_wA) ztV>Z9h4m>S>S#zaAHLTgQB_C$r@D}WMmqUCPca|mn0bmQ-Aa^cQmQUYKL6uB^G=>w zc=&+1RPt1PC!c(N?={YkGYfw#5uF;(W?~KYT495+ja5Kh7`UL(5bhjDLUZ zJoD}i=3kpa#BP^)e_pA&FzGx|br#Rtgha?b)eb0Cj~M^|)82pPpWPCBq4*uex=huh z+$5ggeJ=Te|6uls#NShO566NyDO4Y&G`Sg0+e`EcH=cBsqi0ata zVx=u(EgEc@>%wHqm}7%2bG}WsjJZ#;wGq#wv5!r*j6H3#W$aIbEpx3OvsF$1ozB@X z9JqZ4?OTKSd`2ofCzJW;Z8VLe&iVT!Y^lP$%H=b?=(XrCdbiL)RKMi9?1XoR{JZGx zPUf}2f8LF(Y>hH+A6E2gA)aH2rT%`;xS6X|9dq#5_q)A=NgLZcKc{8ppq|0KzOzYxB;iGQmV(%OR8#bfW~_T|@XO8iFI|r74`sG9D^&+g zwlYo3gMB z#GiMPJ8*Tx?IES=puv{geJg?EM>LnjO{w}yV6(M>y}F6-6n<+*k6=$KRo`5;+1kn? zZ{?rVt=v(Rs&8d&w!WjDio?u8>A%uNPt|R)clWRLR!6b7Kkw`cRbOwl`Sp3m#YiZA zZH{?L?A&vktxr*3GYd?e#9L%~qsoYQGt|fntsz>*yr249`b#xE4 z)rI%($-PTzR;jws;um~)iAn$1F*7E0k9sXrb%#-7_htBmnN!oQ#Jw!iBN}YE zdmSy7?{{Nv4raWR_CuSe v>bY^7E%a^Vm+*8qW>?Wu{BqB2wzfNo!XETDDDjcJDpe1Aj@!Z=C20(cTJ+NTcPNhNq!*Zb9_z{;OS$uBoy5+ ze!mmtPxP^>d#|bUHoYEDbTgYofAd<;i}Ppt7rOruMd!ID_<7#X-0w`E-#0)I5(!0T z8h;A^ZmF+SX?KsyNb{af>5UEahV~xKR+>NjMpVw?xdowD;5*I3-vT+FKzng0+t{`P X={m>H`|O+o6IJf+fTA-cTi3-87aADO literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l6.gds b/testdata/algo/hc_test_l6.gds new file mode 100644 index 0000000000000000000000000000000000000000..38ff58c9c19fde4e6e174cb1b1fc00e994f9dc3e GIT binary patch literal 924 zcmaKqJ4*vW6ot<|GFjsqq67=s!a}=%2!aSA7%db7TBHc6EG#W8Ez+HIWuSGzMx>+B?Ok8e1s1dlbA&+`VY@mh0Q#W zbrLJ9M~hoGXKzdEmvg5lPefI~nFbPBq3D|N{?-9eBOuc391^wLAw_Ql@&1on_E(5BTQy*0H>Bu|$}hhi z=FhR#xe|Llx&`)MbE18U&Wz?C@e?&Sh`ydRxpqyW&nuzmnsI(0>GA%ICcxdtXi+G- zVf1|`jGyRzS=U}u$8Boer|70PvHs?{?ia_;^p17?LyFFQjq!88@44QY9=|UDKOho{ z&NTic{+&`!sp9qyr;*}0ozN5O>j~}NnW;2440hC%#eMUGM4sO?pE+C40qI)rwIOHA Z;aSddf#|99=d8KqWuNG^GlUQ literal 0 HcmV?d00001 From 111a1acdfce6eac08f9d21226006068faf8e354e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 4 Dec 2018 23:32:42 +0100 Subject: [PATCH 080/335] WIP: added test cases for multi-level cross-hierarchy interactions. --- testdata/algo/hc_test_au7.gds | Bin 0 -> 5390 bytes testdata/algo/hc_test_au8.gds | Bin 0 -> 6282 bytes testdata/algo/hc_test_l7.gds | Bin 0 -> 1134 bytes testdata/algo/hc_test_l8.gds | Bin 0 -> 1262 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 testdata/algo/hc_test_au7.gds create mode 100644 testdata/algo/hc_test_au8.gds create mode 100644 testdata/algo/hc_test_l7.gds create mode 100644 testdata/algo/hc_test_l8.gds diff --git a/testdata/algo/hc_test_au7.gds b/testdata/algo/hc_test_au7.gds new file mode 100644 index 0000000000000000000000000000000000000000..d7e070c7d2bf20fc88afb69c010a14a3c075ae83 GIT binary patch literal 5390 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 SK~}Drs8%6*zU<|29Q*-KsTh9% literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l8.gds b/testdata/algo/hc_test_l8.gds new file mode 100644 index 0000000000000000000000000000000000000000..ce243afd2da000419872f1c9ec022555c98e049d GIT binary patch literal 1262 zcma)+KWh|G6vfZZ%)XuIC~F7^VGs+o3P_9~0TB`{vKUB;EhJc^u(xHz+|x-UDd4U}sKH&208BJv z@_gGapf(X+rYn)$z+gXD$cv2R3J~80>SIRJmDT>kH)J;_mFI8C z4>VG_e@6+(^l`OqG~F|Neq81!|2)>-D>b+8b)TmD*;M*_pLAZ5Uzonv{+Bde=33#G zd4HFFHw&Jv0a;EOO&5B91OMC7)&nG0WJ^bNU)^|3W|QUhS*hzkSu<8a6AoQ*$|_22 z@9) Date: Tue, 4 Dec 2018 23:33:04 +0100 Subject: [PATCH 081/335] WIP: added test cases for multi-level cross-hierarchy interactions. --- src/db/unit_tests/dbHierNetworkProcessorTests.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index e92130442..6ed5f38ad 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -575,3 +575,13 @@ TEST(46_HierClusters) { run_hc_test (_this, "hc_test_l6.gds", "hc_test_au6.gds"); } + +TEST(47_HierClusters) +{ + run_hc_test (_this, "hc_test_l7.gds", "hc_test_au7.gds"); +} + +TEST(48_HierClusters) +{ + run_hc_test (_this, "hc_test_l8.gds", "hc_test_au8.gds"); +} From e894724605617ee1ae95607cd0117f0a51e1dab7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 5 Dec 2018 23:14:06 +0100 Subject: [PATCH 082/335] Some more debugging and more test cases. Net processor starts getting stable ... --- src/db/db/dbHierNetworkProcessor.cc | 23 ++++++++---------- src/db/db/dbHierNetworkProcessor.h | 6 +++++ .../unit_tests/dbHierNetworkProcessorTests.cc | 15 ++++++++++++ testdata/algo/hc_test_au10.gds | Bin 0 -> 3484 bytes testdata/algo/hc_test_au11.gds | Bin 0 -> 15224 bytes testdata/algo/hc_test_au9.gds | Bin 0 -> 2636 bytes testdata/algo/hc_test_l10.gds | Bin 0 -> 1048 bytes testdata/algo/hc_test_l11.gds | Bin 0 -> 1076 bytes testdata/algo/hc_test_l9.gds | Bin 0 -> 960 bytes 9 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 testdata/algo/hc_test_au10.gds create mode 100644 testdata/algo/hc_test_au11.gds create mode 100644 testdata/algo/hc_test_au9.gds create mode 100644 testdata/algo/hc_test_l10.gds create mode 100644 testdata/algo/hc_test_l11.gds create mode 100644 testdata/algo/hc_test_l9.gds diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index f3db932aa..b424ed4cd 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -740,7 +740,6 @@ private: hier_clusters *mp_tree; const cell_clusters_box_converter *mp_cbc; const db::Connectivity *mp_conn; - connector_map m_connectors; std::map *> m_cm2join_map; std::list > m_cm2join_sets; @@ -846,31 +845,29 @@ private: ClusterInstance k1 = make_path (i->id (), p1); ClusterInstance k2 = make_path (j->id (), p2); - typename connector_map::iterator x1 = m_connectors.find (k1); - typename connector_map::iterator x2 = m_connectors.find (k2); + id_type x1 = mp_cell_clusters->find_cluster_with_connection (k1); + id_type x2 = mp_cell_clusters->find_cluster_with_connection (k2); - if (x1 == m_connectors.end ()) { + if (x1 == 0) { - if (x2 == m_connectors.end ()) { + if (x2 == 0) { id_type connector = mp_cell_clusters->insert_dummy (); - m_connectors [k1] = connector; mp_cell_clusters->add_connection (connector, k1); mp_cell_clusters->add_connection (connector, k2); } else { - mp_cell_clusters->add_connection (x2->second, k1); + mp_cell_clusters->add_connection (x2, k1); } - } else if (x2 == m_connectors.end ()) { + } else if (x2 == 0) { - mp_cell_clusters->add_connection (x1->second, k2); + mp_cell_clusters->add_connection (x1, k2); - } else if (x1->second != x2->second) { + } else if (x1 != x2) { - mp_cell_clusters->join_cluster_with (x1->second, x2->second); - mp_cell_clusters->remove_cluster (x2->second); - x2->second = x1->second; + mp_cell_clusters->join_cluster_with (x1, x2); + mp_cell_clusters->remove_cluster (x2); } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 69918db97..1c57b9513 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -342,6 +342,12 @@ public: // .. nothing yet .. } + ClusterInstance () + : m_id (0), m_inst () + { + // .. nothing yet .. + } + /** * @brief Gets the cluster ID */ diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 6ed5f38ad..fab9c5d49 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -585,3 +585,18 @@ TEST(48_HierClusters) { run_hc_test (_this, "hc_test_l8.gds", "hc_test_au8.gds"); } + +TEST(49_HierClusters) +{ + run_hc_test (_this, "hc_test_l9.gds", "hc_test_au9.gds"); +} + +TEST(50_HierClusters) +{ + run_hc_test (_this, "hc_test_l10.gds", "hc_test_au10.gds"); +} + +TEST(51_HierClusters) +{ + run_hc_test (_this, "hc_test_l11.gds", "hc_test_au11.gds"); +} diff --git a/testdata/algo/hc_test_au10.gds b/testdata/algo/hc_test_au10.gds new file mode 100644 index 0000000000000000000000000000000000000000..d7c92fb935d018da10d349f0310b76a1a850054f GIT binary patch literal 3484 zcmbW3J4{qj5QcyDaanaC3ldSYg@v6YN@AiW#wc1~G(tgPh=qlvg@uI$1qqmlF@YE# zD31`u2)aIEEGR4}jG>^gu&|&Y#==(&3MeY!_|LtJ=WcLu;wH3s*fb{%S6LSX2b+!^ zLn;FV6$z9fs7wH;%O&|Y_A2Y)KNEm5b}3Z_U7!5;AM1l{^1GS39IHkhFzR~! z&3MetEl%s3vDU@kstXZ|xp=+ncWx=`oa+aYTYz+pQgvbJ_48+)o6S2XTx@f0VZl@N z*?j5s-zT}g-8td>YhcYuPt|AJi`FONIZwXh&YWV*Wa=o_KlfCfjNgB3y>oMo&i(u( zWbWpi`!TLmU07;8P@duStwMk`i*+qZ)kDUAKk4Il?%Q6;y-?x~bG=N}!@ViFey$+< z#rVlgujJoTb?$44_1y0%<~tYh*Ha*ycTTA~8C#!xACKQye^Jc`ta;XPtbdBUjJ0U+ zit0i4f0ep0c^UWE;AP%#lb3PtOL={W@1wDgOv)4+z2sNQ)k;I(=Lo6Fjz&C&;R(>%KP3cq`vz59LA_T9qB-@hwf z`E)~T=(_CN`wXZk6!qLYzwduy3}fB68yMUPC>gunLzI)EnZ*N-6O`v}Y=U0r)LeTB z5LWUu@0;>eom`ROzjZHl*)?0SoIY3yOrPp3h)k%}rwF#Nd%spAo?izUMsXA%$8pr_aFVKVB zM8DewPt{42R~M(|(kq}VAML^AHl^wzOo)E=-?hE zJAsS5$B3$jGlh$h z*a1B3_f(xUd5xT4Zdnh)S=MXekf-XT$?IOTbDvoE!kOp5lTJ_7Nt2hvBTuFyCs_YI mz=Nz(^^hj7;apgY7|umKKFS`Do|gPc4KH6`;l(MKO5ry+f@D1a literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au11.gds b/testdata/algo/hc_test_au11.gds new file mode 100644 index 0000000000000000000000000000000000000000..9552f7549880432170d0ea8fa0f69d2708f1ef60 GIT binary patch literal 15224 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 rK7V?$bI=WOnm~LQ+|zv`k@k(^FGuq=4(M zqXsun2hbi&_!(Ta`~2qxU<{TTLoom3(|`E8~6s`y2|p4HqdHSQwUCCy&crl)^rRqMt0$#hBcU(xKWYY%_c z`!(}D==t+0kmVvqvy*{;@*#{rvj2R~JN7*LIM_d3T9LgNq2>F*>c2-{L}^9VI6^DB z-ceeSweO|%F|1?cK916g+|yB7k^3`3E4tTrY5ik5r_RkxU*q=emFfACE^$7r`Tpm3Q+`*^qGGL|^XFjKoZ&6*_;Yy5;(=*g2MX z&UI@wZ}~Z;9NYlE1f9;%DyY0%JS{7|l*bX}R;j@Hzt~$1-4GzM|R5 zC@qbv*70%anUL=Ivq5QgGD-`2%@v<$Jn{K4nw^Z&D(eiT=4pVl*U0)8cG7Eg{YLm( zC9|Aw)7Dnwn=&$0GMb%?icB+{-1Ze1Jxs0OHai)m*boqw4KYEc5J)5vF@wM%moO3>6O#x8 z0zn93e*glppb!X>ZEV28<6XO>C0WUuG@tf+_q}`feFp(u--WB`v1d52kirtQ@HcE- z_RQZJyT!>`gM zVa{N*pK3(k4iVJ=V;-=Qib|J^_ID47+Afjg;E1T-52*CE8}08k*^=e$djXZ+ zuKe-q!TGt?7fxc|r5@@?AZGLUf+WD* z$5csC=^n%T8$ta<@2fIjK+I_sc`PfJX!#p?bB{TzUUR&;jr!=hyd1BaE&I#DTeo@x#2dePnI?V! D6Z#a0 literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l11.gds b/testdata/algo/hc_test_l11.gds new file mode 100644 index 0000000000000000000000000000000000000000..21037004cb4160a5f1e0c54cc2ba4e1999ffb412 GIT binary patch literal 1076 zcma)*KP&`M5XQg#dy6H9qmV5~loAmVxkMzKg1Fo%t`JculuA)4uGJ|8g^oh0P%1E+R0@)IEGS=)o99P;~&5gkJ0Jq(Q0k^{hFqF24J}deH#!&oKT?o4q zzx!#Q=*uUfeZcAmoQzU+VY0uzPE-wugrA#4udfkRuLjBfmn-&{iG=lApk*bZ>eccO zzZvbHYklBOtTt%p*?-O(ZBlh+eE%^&QEis!{ZYuhs}a4ODODFH`GJ(j{|AKtPakb1 zrRpK$`K_paqUQNsVxjQcIpZ0{9%Gvqm)z$jW552Hr&L_vcD3<+=#rb^_8A<*51i?x zW-PB;?nx_xv;||?2ilL7L^o^UI?NT1Q4V9|(v?P#A29~_e7{`5Xpm_4I$UcEkL!@V Zi^t*2k{27xK<^QZEYF6iYu%%5;~TSeAr$}s literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l9.gds b/testdata/algo/hc_test_l9.gds new file mode 100644 index 0000000000000000000000000000000000000000..ebbf2798e476f765014aa86d88bc21660f0ba0fa GIT binary patch literal 960 zcma)*y-UMT6va=zUZbW`wKzxy2gibR5>m*IdI?qUgxUW0TlP1Jgs*qN$ZkT_JI!By zJJ~4d_acUhJxX2H5x?AU&M{L%|NKt9w#{*k^8YX?e} Date: Fri, 7 Dec 2018 23:21:38 +0100 Subject: [PATCH 083/335] Net processor: Added timing, rearrangement as preparation for parallelization --- src/db/db/dbHierNetworkProcessor.cc | 98 ++++++++++++++++++++++++++--- src/db/db/dbHierNetworkProcessor.h | 3 + 2 files changed, 93 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index b424ed4cd..82c1a89d7 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -28,6 +28,9 @@ #include "dbPolygon.h" #include "dbPolygonTools.h" #include "dbBoxScanner.h" +#include "tlProgress.h" +#include "tlLog.h" +#include "tlTimer.h" #include #include @@ -1069,21 +1072,96 @@ template void hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) { - // already done - don't do again - if (m_per_cell_clusters.find (cell.cell_index ()) != m_per_cell_clusters.end ()) { - return; + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Computing shape clusters"))); + + std::set called; + cell.collect_called_cells (called); + called.insert (cell.cell_index ()); + + // first build all local clusters + + { + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing local shape clusters"))); + tl::RelativeProgress progress (tl::to_string (tr ("Computing local clusters")), called.size (), 1); + + for (std::set::const_iterator c = called.begin (); c != called.end (); ++c) { + build_local_cluster (layout, layout.cell (*c), shape_flags, conn); + ++progress; + } } - // Build local clusters + // build the hierarchical connections bottom-up and for all cells whose children are computed already + + { + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing hierarchical shape clusters"))); + tl::RelativeProgress progress (tl::to_string (tr ("Computing hierarchical clusters")), called.size (), 1); + + std::set done; + std::vector todo; + for (db::Layout::bottom_up_const_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) { + + if (called.find (*c) != called.end ()) { + + bool all_available = true; + const db::Cell &cell = layout.cell (*c); + for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end () && all_available; ++cc) { + all_available = (done.find (*cc) != done.end ()); + } + + if (all_available) { + todo.push_back (*c); + } else { + tl_assert (! todo.empty ()); + build_hier_connections_for_cells (cbc, layout, todo, conn); + done.insert (todo.begin (), todo.end ()); + todo.clear (); + todo.push_back (*c); + } + + ++progress; + + } + + } + + build_hier_connections_for_cells (cbc, layout, todo, conn); + } +} + +template +void +hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) +{ + std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); + if (tl::verbosity () >= 30) { + tl::log << msg; + } + tl::SelfTimer (tl::verbosity () >= 31, msg); connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; local.build_clusters (cell, shape_flags, conn); +} - // handle connections inside child cells in a bottom-up fashion - - for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end (); ++cc) { - do_build (cbc, layout, layout.cell (*cc), shape_flags, conn); +template +void +hier_clusters::build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn) +{ + for (std::vector::const_iterator c = cells.begin (); c != cells.end (); ++c) { + build_hier_connections (cbc, layout, layout.cell (*c), conn); } +} + +template +void +hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn) +{ + std::string msg = tl::to_string (tr ("Computing hierarchical clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); + if (tl::verbosity () >= 30) { + tl::log << msg; + } + tl::SelfTimer (tl::verbosity () >= 31, msg); + + connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; // NOTE: this is a receiver for both the child-to-child and // local to child interactions. @@ -1109,6 +1187,8 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou // handle instance to instance connections { + tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Instance to instance treatment"))); + db::box_scanner bs; for (std::vector::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) { @@ -1121,6 +1201,8 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou // handle local to instance connections { + tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Local to instance treatment"))); + db::box_scanner2, unsigned int, db::Instance, unsigned int> bs2; for (typename connected_clusters::const_iterator c = local.begin (); c != local.end (); ++c) { diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 1c57b9513..108b185e1 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -549,6 +549,9 @@ public: void clear (); private: + void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); + void build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn); + void build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn); void do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); std::map > m_per_cell_clusters; From df44c364ea6deef8422a710011652513ba6369ae Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 8 Dec 2018 01:55:02 +0100 Subject: [PATCH 084/335] WIP: network processor: net backannotation into original hierarchy. --- src/db/db/dbHierNetworkProcessor.cc | 141 ++++++++++++++++++ src/db/db/dbHierNetworkProcessor.h | 129 ++++++++++++++++ .../unit_tests/dbHierNetworkProcessorTests.cc | 72 +++++++++ testdata/algo/hc_test_au10b.gds | Bin 0 -> 3032 bytes testdata/algo/hc_test_au1b.gds | Bin 0 -> 1386 bytes testdata/algo/hc_test_au2b.gds | Bin 0 -> 2218 bytes testdata/algo/hc_test_au3b.gds | Bin 0 -> 2346 bytes testdata/algo/hc_test_au4b.gds | Bin 0 -> 2390 bytes testdata/algo/hc_test_au5b.gds | Bin 0 -> 2262 bytes testdata/algo/hc_test_au6b.gds | Bin 0 -> 3484 bytes testdata/algo/hc_test_au7b.gds | Bin 0 -> 3694 bytes testdata/algo/hc_test_au8b.gds | Bin 0 -> 3950 bytes testdata/algo/hc_test_au9b.gds | Bin 0 -> 1664 bytes 13 files changed, 342 insertions(+) create mode 100644 testdata/algo/hc_test_au10b.gds create mode 100644 testdata/algo/hc_test_au1b.gds create mode 100644 testdata/algo/hc_test_au2b.gds create mode 100644 testdata/algo/hc_test_au3b.gds create mode 100644 testdata/algo/hc_test_au4b.gds create mode 100644 testdata/algo/hc_test_au5b.gds create mode 100644 testdata/algo/hc_test_au6b.gds create mode 100644 testdata/algo/hc_test_au7b.gds create mode 100644 testdata/algo/hc_test_au8b.gds create mode 100644 testdata/algo/hc_test_au9b.gds diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 82c1a89d7..6bad4c7fb 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1241,7 +1241,148 @@ hier_clusters::clusters_per_cell (db::cell_index_type cell_index) return c->second; } +template +static +void put_or_propagate (const hier_clusters &hc, incoming_cluster_connections &inc, size_t cluster_id, const local_cluster &cluster, db::Layout &layout, db::cell_index_type ci, const std::map &lm, const db::ICplxTrans &trans) +{ + db::Cell &target_cell = layout.cell (ci); + + if (cluster_id > 0 && inc.has_incoming (ci, cluster_id)) { + + typedef std::pair reference_type; + std::map references; + + for (db::Cell::parent_inst_iterator pi = target_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + db::Instance i = pi->child_inst (); + for (db::CellInstArray::iterator ii = i.cell_inst ().begin (); ! ii.at_end (); ++ii) { + references.insert (std::make_pair (reference_type ((*pi).parent_cell_index (), db::InstElement (i, ii)), false)); + } + } + + const typename incoming_cluster_connections::incoming_connections &connections = inc.incoming (ci, cluster_id); + for (typename incoming_cluster_connections::incoming_connections::const_iterator x = connections.begin (); x != connections.end (); ++x) { + typename std::map::iterator r = references.find (reference_type (x->parent_cell (), x->inst ())); + if (r != references.end () && ! r->second) { + put_or_propagate (hc, inc, x->parent_cluster_id (), cluster, layout, r->first.first, lm, r->first.second.complex_trans () * trans); + r->second = true; + } + } + + for (typename std::map::const_iterator r = references.begin (); r != references.end (); ++r) { + if (! r->second) { + put_or_propagate (hc, inc, 0, cluster, layout, r->first.first, lm, r->first.second.complex_trans () * trans); + } + } + + } else { + + for (typename std::map::const_iterator m = lm.begin (); m != lm.end (); ++m) { + db::Shapes shapes; + for (typename local_cluster::shape_iterator s = cluster.begin (m->first); ! s.at_end (); ++s) { + shapes.insert (*s); + } + tl::ident_map pm; + target_cell.shapes (m->second).insert_transformed (shapes, trans, pm); + } + + } +} + +template +void +hier_clusters::return_to_hierarchy (db::Layout &layout, db::Cell &cell, const std::map &lm) const +{ + incoming_cluster_connections inc (layout, cell, *this); + + for (db::Layout::bottom_up_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) { + const db::connected_clusters &cc = clusters_per_cell (*c); + for (typename db::connected_clusters::const_iterator lc = cc.begin (); lc != cc.end (); ++lc) { + put_or_propagate (*this, inc, lc->id (), *lc, layout, *c, lm, db::ICplxTrans ()); + } + } +} + // explicit instantiations template class DB_PUBLIC hier_clusters; +// ------------------------------------------------------------------------------ +// incoming_cluster_connections implementation + +template +incoming_cluster_connections::incoming_cluster_connections (const db::Layout &layout, const db::Cell &cell, const hier_clusters &hc) + : mp_layout (const_cast (&layout)), mp_hc (const_cast *> (&hc)) +{ + cell.collect_called_cells (m_called_cells); + m_called_cells.insert (cell.cell_index ()); +} + +template +bool +incoming_cluster_connections::has_incoming (db::cell_index_type ci, size_t cluster_id) const +{ + std::map >::const_iterator i = m_incoming.find (ci); + if (i == m_incoming.end ()) { + ensure_computed (ci); + i = m_incoming.find (ci); + tl_assert (i != m_incoming.end ()); + } + + tl_assert (i != m_incoming.end ()); + return (i->second.find (cluster_id) != i->second.end ()); +} + +template +const typename incoming_cluster_connections::incoming_connections & +incoming_cluster_connections::incoming (db::cell_index_type ci, size_t cluster_id) const +{ + std::map >::const_iterator i = m_incoming.find (ci); + if (i == m_incoming.end ()) { + ensure_computed (ci); + i = m_incoming.find (ci); + tl_assert (i != m_incoming.end ()); + } + + std::map::const_iterator ii = i->second.find (cluster_id); + if (ii != i->second.end ()) { + return ii->second; + } else { + static incoming_connections empty; + return empty; + } +} + +template +void +incoming_cluster_connections::ensure_computed (db::cell_index_type ci) const +{ + tl_assert (mp_layout.get () != 0); + m_incoming.insert (std::make_pair (ci, std::map ())); + + const db::Cell &cell = mp_layout->cell (ci); + for (db::Cell::parent_cell_iterator pc = cell.begin_parent_cells (); pc != cell.end_parent_cells (); ++pc) { + if (m_called_cells.find (*pc) != m_called_cells.end ()) { + ensure_computed_parent (*pc); + } + } + + m_called_cells.erase (ci); +} + +template +void +incoming_cluster_connections::ensure_computed_parent (db::cell_index_type ci) const +{ + ensure_computed (ci); + + const connected_clusters &cc = ((const hier_clusters *) mp_hc.get ())->clusters_per_cell (ci); + for (typename connected_clusters::connections_iterator x = cc.begin_connections (); x != cc.end_connections (); ++x) { + for (typename connected_clusters::connections_type::const_iterator xx = x->second.begin (); xx != x->second.end (); ++xx) { + m_incoming [xx->inst ().inst_ptr.cell_index ()][xx->id ()].push_back (IncomingClusterInstance (ci, x->first, xx->inst ())); + } + } +} + +// explicit instantiations +template class DB_PUBLIC incoming_cluster_connections; + } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 108b185e1..e102a5d26 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -443,6 +443,7 @@ public: typedef std::list connections_type; typedef typename local_clusters::box_type box_type; typedef connected_clusters_iterator all_iterator; + typedef typename std::map::id_type, connections_type>::const_iterator connections_iterator; /** * @brief Constructor @@ -488,6 +489,24 @@ public: return connected_clusters_iterator (*this); } + /** + * @brief Begin iterator for the connections + * + * The iterated object is a pair or (cluster id, connections_type). + */ + connections_iterator begin_connections () const + { + return m_connections.begin (); + } + + /** + * @brief Begin iterator for the connections + */ + connections_iterator end_connections () const + { + return m_connections.end (); + } + private: template friend class connected_clusters_iterator; @@ -519,6 +538,7 @@ template class cell_clusters_box_converter; */ template class DB_PUBLIC hier_clusters + : public tl::Object { public: typedef typename local_cluster::box_type box_type; @@ -543,6 +563,18 @@ public: */ connected_clusters &clusters_per_cell (db::cell_index_type cell_index); + /** + * @brief Writes the net shapes back to the original hierarchy + * + * The layout object is supposed to be the original layout or one with identical cell indexes. + * "lm" is a layer mapping table from the connection layer indexes to the target layer + * indexes. + * + * The backannotation process usually involves propagation of shapes up in the hierarchy + * to resolve variants. + */ + void return_to_hierarchy (db::Layout &layout, db::Cell &cell, const std::map &lm) const; + /** * @brief Clears this collection */ @@ -557,6 +589,103 @@ private: std::map > m_per_cell_clusters; }; +/** + * @brief A connection to a cluster from a parent cluster + */ +class DB_PUBLIC IncomingClusterInstance +{ +public: + IncomingClusterInstance (db::cell_index_type pc, size_t parent_cluster_id, const db::InstElement &inst) + : m_parent_cell (pc), m_parent_cluster_id (parent_cluster_id), m_inst (inst) + { + // .. nothing yet .. + } + + IncomingClusterInstance () + : m_parent_cell (0), m_parent_cluster_id (0), m_inst () + { + // .. nothing yet .. + } + + /** + * @brief Gets the cell index of the parent cell + */ + size_t parent_cell () const + { + return m_parent_cell; + } + + /** + * @brief Gets the cluster ID from which the cluster is connected to + * The parent cluster lives in the parent cell + */ + size_t parent_cluster_id () const + { + return m_parent_cluster_id; + } + + /** + * @brief Gets the instance path + */ + const db::InstElement &inst () const + { + return m_inst; + } + + /** + * @brief Equality + */ + bool operator== (const IncomingClusterInstance &other) const + { + return m_parent_cluster_id == other.m_parent_cluster_id && m_parent_cell == other.m_parent_cell && m_inst == other.m_inst; + } + + /** + * @brief Less operator + */ + bool operator< (const IncomingClusterInstance &other) const + { + if (m_parent_cluster_id != other.m_parent_cluster_id) { + return m_parent_cluster_id < other.m_parent_cluster_id; + } + if (m_parent_cell != other.m_parent_cell) { + return m_parent_cell < other.m_parent_cell; + } + return m_inst < other.m_inst; + } + +private: + db::cell_index_type m_parent_cell; + size_t m_parent_cluster_id; + db::InstElement m_inst; +}; + +/** + * @brief A class holding the parent relationships for clusters of cells + * + * This class can be used to quickly identify the connections made to a specific cluster from a parent cluster. + */ +template +class incoming_cluster_connections +{ +public: + typedef std::list incoming_connections; + + incoming_cluster_connections (const db::Layout &layout, const db::Cell &cell, const hier_clusters &hc); + + bool has_incoming (db::cell_index_type ci, size_t cluster_id) const; + const incoming_connections &incoming (db::cell_index_type ci, size_t cluster_id) const; + +private: + mutable std::set m_called_cells; + mutable std::map > m_incoming; + tl::weak_ptr mp_layout; + tl::weak_ptr > mp_hc; + + void ensure_computed (db::cell_index_type ci) const; + void ensure_computed_parent (db::cell_index_type ci) const; +}; + } #endif diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index fab9c5d49..d527e4b7d 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -546,57 +546,129 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/" + au_file); } +static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::string &file, const std::string &au_file) +{ + db::Layout ly; + unsigned int l1 = 0, l2 = 0, l3 = 0; + + { + db::LayerProperties p; + db::LayerMap lmap; + + p.layer = 1; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l1 = ly.insert_layer ()); + ly.set_properties (l1, p); + + p.layer = 2; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l2 = ly.insert_layer ()); + ly.set_properties (l2, p); + + p.layer = 3; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l3 = ly.insert_layer ()); + ly.set_properties (l3, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/"; + fn += file; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + normalize_layer (ly, l1); + normalize_layer (ly, l2); + normalize_layer (ly, l3); + + // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 + db::Connectivity conn; + conn.connect (l1, l1); + conn.connect (l2, l2); + conn.connect (l3, l3); + conn.connect (l1, l2); + conn.connect (l1, l3); + + db::hier_clusters hc; + hc.build (ly, ly.cell (*ly.begin_top_down ()), db::ShapeIterator::Polygons, conn); + + std::map lm; + lm[l1] = ly.insert_layer (db::LayerProperties (101, 0)); + lm[l2] = ly.insert_layer (db::LayerProperties (102, 0)); + lm[l3] = ly.insert_layer (db::LayerProperties (103, 0)); + hc.return_to_hierarchy (ly, ly.cell (*ly.begin_top_down ()), lm); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/" + au_file); +} + TEST(41_HierClusters) { run_hc_test (_this, "hc_test_l1.gds", "hc_test_au1.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l1.gds", "hc_test_au1b.gds"); } TEST(42_HierClusters) { run_hc_test (_this, "hc_test_l2.gds", "hc_test_au2.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l2.gds", "hc_test_au2b.gds"); } TEST(43_HierClusters) { run_hc_test (_this, "hc_test_l3.gds", "hc_test_au3.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l3.gds", "hc_test_au3b.gds"); } TEST(44_HierClusters) { run_hc_test (_this, "hc_test_l4.gds", "hc_test_au4.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l4.gds", "hc_test_au4b.gds"); } TEST(45_HierClusters) { run_hc_test (_this, "hc_test_l5.gds", "hc_test_au5.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l5.gds", "hc_test_au5b.gds"); } TEST(46_HierClusters) { run_hc_test (_this, "hc_test_l6.gds", "hc_test_au6.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l6.gds", "hc_test_au6b.gds"); } TEST(47_HierClusters) { run_hc_test (_this, "hc_test_l7.gds", "hc_test_au7.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l7.gds", "hc_test_au7b.gds"); } TEST(48_HierClusters) { run_hc_test (_this, "hc_test_l8.gds", "hc_test_au8.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l8.gds", "hc_test_au8b.gds"); } TEST(49_HierClusters) { run_hc_test (_this, "hc_test_l9.gds", "hc_test_au9.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l9.gds", "hc_test_au9b.gds"); } TEST(50_HierClusters) { run_hc_test (_this, "hc_test_l10.gds", "hc_test_au10.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l10.gds", "hc_test_au10b.gds"); } TEST(51_HierClusters) { run_hc_test (_this, "hc_test_l11.gds", "hc_test_au11.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l4.gds", "hc_test_au4b.gds"); } diff --git a/testdata/algo/hc_test_au10b.gds b/testdata/algo/hc_test_au10b.gds new file mode 100644 index 0000000000000000000000000000000000000000..56a41f98c764b020ab72624f4cb15586a5c75a8f GIT binary patch literal 3032 zcma);O>9h27>3`@&+T9;qr_ikVPRoWK_WrYh`$BVV9|w$g@vC@7Zw&4Bt;u7O*GC?XFzW4p^`Odkg2#`o@ zN4_di{RUZNP=&>)jsK2JB52sO4w#ipuU^}}V)N4*L#sCQFS&AQM^(cf&0@XJeJ$}UIM zhnp+>iTErh-*GN?vt}~YK>rg*)yeqt$Na{OY&7Qk2O;Nf#F%dbO4WrvKTw_F_(CDT zRfcJeO4UQg^OLTAV@B(34@T>y?r^Rnsvh>n>mMmezgRz+Daiags?NT8{OtED&Ueo8 zM<0-7FO{m3q5mJNG!q1kTetBsrm}=uyEC$~6S|v#***3i91A$M48i=WW{FF$&%I^~ z`3bLW-c{!kD?G?|! zR?BX$@So=npV|Qkr*j=&dgZ7(8Sg`d|19@k4cDNsb^y3oc2u48`fqXXja~tST(7u0 z=%_mB_1|nW=5s$F99s(9>2g$^^!i(xY`#$P+xi_PKN|P?j~=x7Ldieg;VAjhxYysc z#=f_NK@qslOr^ZnqH(YP>RaxCgMe^Omi~H2)k&|vo%`nJ3!pu3_y6fbO4UPp{igMqw1vB-?NkTabpUH*~8KXN7YHMzjvE4AJ~84 z&=cT(i=*nK*MFqJ<_jgib-APDN8?`qu{AbdDETLOo+>3j8u$9o4a&L2`6H{jj=I@L zrRt>Df2_#$k!4Pi`=|vtQBtZN((CW-v)A>qhvL8YSgHC1|Gq!hkDTGMex~&U7Zy0G zPR8>q{GAK!dKb#Rm-nbr&gE#_>z8#SXMfL5&SNKVn{#2QdPuLonB()k0E#(2H*3Gy zrc^zo*Dvcs@{csw+}3fW>XYXmhRrz)n|qXNM(U9FrqmUUd-J<9z)aRGoHqzuWB-+^ slU{%OTVvjr`Tiq64ROACjwn?pz5X))_mb;`;(J(dR9*f2KO~jHFTNxhQ~&?~ literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au1b.gds b/testdata/algo/hc_test_au1b.gds new file mode 100644 index 0000000000000000000000000000000000000000..e1f2615cf0fe676636519693db3807ef6812de1b GIT binary patch literal 1386 zcmbW0y-EW?6opTAH=7lsD5TuNB8?ER6hQ>VLNTC43bC^A=LIY+()tF1DeP=4Ei5g> z$_KEp5GxA{A&Eu}9`7W%%xtlOQ!HON;mh8+bAlK_u#L13l%7#Wf&ykybw48s;`;gu zPzj4m%O~?2cNcF9tJibqXFCm~#Xr0_ZftG=Q3Avhf&6kj833qFg=GIpAD~ckAI%LB zP3QP*kw0aUEg+l$iWQ~ihWUQ?h-{~%VgH!y^(-}Sr}_TJz4%Siu=@av?OJNyZf5+z zo?m8tkxlFk8P&zVm4l9`xzIhoV!Z literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au2b.gds b/testdata/algo/hc_test_au2b.gds new file mode 100644 index 0000000000000000000000000000000000000000..cd520f4de6ceac1d17ea49facc4e8e322a11f9c9 GIT binary patch literal 2218 zcmbW2KWI}?6vj_pUS3{flqek>-r&$dAs|9Q1rgK?iUl1qh&VX-XL0D@(7~ZY7dM9v zf*m>tIyiLb(80l>NC!s;hYrOlSdr4CZEF4;&-ZQ)_uk?aOyKbg-^431D4Dr+ooKRHx_a&9#oM3X?OblGonL-)ze!ms>`Rj5#s!K~ zqU3lae>XW15mCKXWbSY2LsY1{kLn=BC+Y8gUSb|rnR$t*c!sDnsZ<>d`}dwRw=-t2 z|AM*OwN$;Gh5ZL##6Qmr?tLSgcxb75d*1U$_WZKfmQ>=AVLT)LbvfvWsteuoJAUTQ zP3GS}L8)Db`S6ocbui>7DyQ;(3M7)-r(;c}>N(x@i+2Ca`&UqV5c|$+J)-LQ-30xe z54bPczc77``dg|l_ZslaeQ!#=Ylgj5qH4yhR9%?+%kbjo!Qa z_x$MKp0r(QQ)<3IR5`0uJ!imgrg#4|XeLQ_Q7Mx=*Fmp-&iW?znsd#)QuQPGcl=}Z zpW`=uqkW=%qg4G!|2(zzH`9+cMpJN3&bo#A8>&tl-uXSh{CCPHHaM}4{90n7oA)8V dcx86v(_e_SD?65|3*Gzk*8iXWmkA!n^anC9_09kQ literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au3b.gds b/testdata/algo/hc_test_au3b.gds new file mode 100644 index 0000000000000000000000000000000000000000..9767fc7a0b68c3b9ab90e5559462d53bce142511 GIT binary patch literal 2346 zcmbW2KWI}?6vj_pUS3{fEU|QOc!Pt3Lqvpv3L>Z(6bp99AmZTQpT(hrLkEWr9dvW( zAlRXUpo2q)4jCLAiga*v=-^PCf)y!E+NRB)Y(3`3D&3o!JH&Y)n&Tv`Z@6*tYUg(0)>ZCd(=#+x=feXKSuvL>yOdDmVd1N zeUj~;?A0gJe_&w&r$WRc4o~3)$#om`H&mTA yo%|iY+>Ysu0Zwe7|1;u7G4FkT@ygki-abOCUE0m4y3pP~XZ`;G!M36?^$op%!eIVVU5L0HfA*VMV~)$lm|38B0Vs7URTqZ+yDyE|sTd=yPZ?9I zd8)os3H$d~>7O=6xVr{4Kk-z3XS%^3`SWwHn~cO$$9RnXWgax6>SX@>IlnR0QDY9a zgp6I)n4h1OstZGYpeg13J|V#D@ zl-Ti*p+4Wqy<}9K4D281+HlzxO4Xiw%Jb)MZuO1#o*o~pBx0{;K1zg+#ey3;!Q z$NGEj>yNSf#9n&qSelehXZ$m&PUiRD;FoTfdMT~5=3dEpl&V_>{p~}X*58>wa(@*# z!(LFTP6qwbZ!+bD>=T^c@>HD+`g@r#nUun#5ul&BRH{w}{O(It?-shLDY=j-S)1Wk z%j&`g{|g^~*E?Vj+Q~S{XE!ANj;eDW<=@}n=aM?Jf(R$RS3=jgg4N0wX3SYGO=83XLRMP!KB%#g@W?g5nAb?JOuv zq@dG+!oq^W!h#qIN(%~#i?N_Ek%R+IIBtCZ%>C}3aV)%K^IhKTyP5gt&3}jy1XD;0 zLC05=kf4Bz=*@pe62udC$AG?Kbo18BtM@*?-MT)$d};B`!%3u(-xtU6?aK%gAU+!K zcjN8=z(`Ngm_}6}pfHkuR2L#FBGf)bHP0qrYN|aAzIpe3Vi3?Mj6f$@H&qw<*6-3TiI?Z_ox62P)p--V^Y60WwI!yNlzJssGOErbdi=m(!=-Dm z;X2Lvic)n;ufNQ*mwE}q_rSRe8C55}{{P6&bUr`jQFg0uoS)oV15TDQs!rzTZ}UrD zrOOG~RdJl1N2xmL^#>n$mtL_3WHc)`J)`QR*DtqS@*Zt4GtYUCSU*SAEj@nM=^On* z*LCF1Ntdb5v+G-3=%0U(d67;hJRApxnI)y_WPbjq`TD2Kxzt%Ga}OapI@@x`llde iZppLK{)4!2&-#Jxb-q6*lyhUh>Q}06Y5nZ>VTfN7tj8Dt literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au6b.gds b/testdata/algo/hc_test_au6b.gds new file mode 100644 index 0000000000000000000000000000000000000000..903d59d5fdeb78546de96d9f28346527e6d5c1bb GIT binary patch literal 3484 zcmaKuJ8TqJ6o!xYF$k7NHG$M972&ONJ-(sLKGCvqoAOmpm5)`QuNnee}hzw>D3mUHoAF=krri zD~Eki6n%O^N)?f)uYm7H{RI&jA1JEsJ@%hS&v^FdIw_?i@@Bu@RNaqN^`=O1Or$*Q zbe#z)l|v7S=CM_rR!U@u>a)}{Bx@0&a%j%iz!{-n#=hM>H0C(CU#=RSULy) zeJr#~*TL-iGk(?W&s1NxN$lOW>Z^NB*U6Ayq_=|S$4C)GUk*<>T~C-DznIQn^~GuK zJ;`||d)=k$No<1t?FGgQ^9L(GasN}gj<^Q=h<6?PJz(6uBNEqCovwq4zgOOxrIjd} z`tmC*ql!EoWX8tK(EW!=RwiHg|4H;-58@Wr%4aZU@|V2Z92eKfwQ#=eu!;oU&ooUXU)VgEXE&VQ^i)``>g^-9>kd&g>Ypg-mT(Rjo86N)`#_L;9C6AzAtikJay>B7xe=>rYEvk|OONhmfDP2Fj81}Q~ z{rB%}>pf1_(GP;_e=*N`L(PIOOO}aF*HMo_|Hes?!3&5xc=WF7KZ_|{AG{FuKUxwQ z6;<-+wCd)?l&+5=B$=L{%U|vhz2piC_T7<`u7g28=lCaj9?7{6BcGhEg8@I{_L#9V zwyz=&W>61Zx{lZe{ZA+0omC}QA6opJu0NU0`M=55KlAkONy}H9P15@t>&NIim>oao zXV2uGlkB_w11Vhx1Agq)6#Yi#TjxW|N7D7T`FYm1#_Y`Hw_e?|Z;`II#zNzx*6*St zqh`qRRh}0qT?Yg6w{`C#Z_0J-A^K=V3p41Y<~cZdm_tx_;o!nVUcFHS(Z) z@4?u(AEOS8uFHe)__&WIcQo?QefFP}yu;b^uphOV%bX%h2@)V8{zVFrqKFU*tRN+Y3k#)i;Uo@14}8Qkjx=U#0(^G$ofld0U3#|0S=~PfZ*b8L8Gj`0%TDKmOs{&U+`W9a#A0 z^rW;vAteaeb!Xf%Ocfr zk=lsUbuw-LU{-a#tx9g5SM7EKU0-jf?H}EO{hTWK;I_!XbfD|&b0zyqm_Nq4j7ZEF zvs19Yfr<9$IvCGCvRBmV1knzb?k!D-f={i{0_sh$w zbmFjN4OHnc>$N`Chuk#Vwd441aW#jk6ga}th&~Sy5ovS~%`?WqYa*{9GEUdQct$0A zW}f3$buhO7GWOp> zHt2`UH$`?V1-ib)f64xJR3k5v^WAwi&~>oz{|XsJ-+4hsPxDK-ij3}_D>77k@%g*O z6XtFBv(j8kx!Rjq$0D;UB8O)J+0A;-<5-pK4=kekFNlykMq!U3Ib8>n_VmFRk*(Y2 z+v)n2S}FObkJ=p@18ar8ovwqi|5B~Jd67XBA?a`5e4y)K(w^^ZJITM;R6aFZr|V#B zU#gY8w?wwXC%N+{k%Ooer|V$Sp6_r?lB`$SI$ig*`ULH5a>E~*rfgXeUV*Lh#B|>^6>OTpzFJ)()K)kG&&qV zw%7|CN1*H27i0gWK2rG}RelLI2eOY~e|=8Z!K6J;0Z*`ayk|A`SWjafOfQ(QSN#Y1 z?o1mQ@9sg%1?hTcI_2L^k1OLwe(Ujd`xfc?%6Q72>qYg?V@8}drT<6PuhDfdF@DaQ z<23Rl)f!?w7+wFr@pG+gj+#%Ny1MfiOF`Gklz;XQk+Dn0@!az0bbYLxzo?Jf2~KYI zPIBkqYl-NT&kv&iQ|8g&J~3{_Q5QZ{{TB2;8Xwr literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au8b.gds b/testdata/algo/hc_test_au8b.gds new file mode 100644 index 0000000000000000000000000000000000000000..816bb74d3e0e3fe3b16118409c820313636551dd GIT binary patch literal 3950 zcmbW4KWr3N5XQ%Md+YW2axsn(mN`Y1l1P9Y@mC6wqKFWRSh188E-aM7h4ZJNpm0Iq z!UaVlrJ!&TDHQJFA_WBnMG6WOQBb6yNZ}C6hso)1%n=)WfXU?h_T8{=Ek9zh(&~5m z_GaIEGxKKNN-TxKgw(nU-OEyuM7rc%>Gyw2Qi#V!Pm2r`OCNsp-Fu(@`cv!uGuIB! z|9Ey>YNfDU9LFCYl_(L3Un}6fakU^KBYj2HttGofx<>rlbW)-{va`KjR{gi4s+UEI z!y=^tqv>SW{?WARdQFwwno(`HGn&3$3)^qrg8i&2`RI3%p2>`+ug~V}3)%WH*9JT> zq3NK%e$QTY;}g}Vt0a84q59;y(R4CoFVdah`P-xjqA$JUM$=RJ;}^5} zt3LjKz9%{F)Yl!Fp2j9tCxfA={)yDCztsTxfOQ~PfD zzbqX+CQ%t#+RuEAhq@4(O09Gn?@g~tUy%Z5n48g;nMXv*EqL>sWA8PQ*WnqX>7c)& zoIN8^y5J&G{vPAa;$0n@4*K?A`QtN+%pfv3k-tp`gZ6S04mgGNg4Joaql~75zWvw! z_*F!M*&(85`6gUxMBkZBBb2`J z#edT$j9dQ~xwYnEbue-ji;S*_9Gl9>YSww~`zmLDcmdgeQH0z(2zyM)XgcWI=VJX9 z@`4vhUmcpuXgcWI=X`zemdGC1ko#_n9D%Qmrh}>dE_{95e2smS`j2zKwHx~+j>kXy z9RKwrfO>*i!ISA5+>_Dc-po?Wd@qmi>^!RM zNfHhm%eqOiVLF+cf6o3sD)%ZXDf#e#TZ_^3)y1&=8oG$x?3(O+M$^}lkiF|d_f9%C zu^)QJRgI>Df%PNj2UHsyj%(*oFR=!r>0rR#by0Rw-p?0YH#eG&4jZ(8JkJh>`~f#n z3z8EVO-F|d+OOXg**^iFfo~!X&(CBueg8z*p54EUyd!HDIggB{qb~)=uP>?A2XTX| z7m&YG8BMPbhVAbm_v$mMB=-PMLq^l11ep)=!b4OOC@R(2S$e^r2w4_8j2VX46 zA4k)-^^crAz7Wz=Gud;4`zIq$)$VzC{JRUTW|Pbx?qMVQk9!`r=N@Ma$o8Du|3=fd zeFb^*cX{(CW7eQ8_fJOCK|g;ydwwCP-iaKSxsL`Aiw;e{6NT*IyDOb95RkrS%`>K7 wChmD~e15sHW|P%v*v@1$9cSsFJ$=1WaX!C~v7Ua5UfQAQD-~ytb5s<`e+V(f<^TWy literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au9b.gds b/testdata/algo/hc_test_au9b.gds new file mode 100644 index 0000000000000000000000000000000000000000..3e89d8674d82a66843142b212dc4141c40e9deb6 GIT binary patch literal 1664 zcmai!FKiT15XQg#_iibB<$AO!AuABb5<)^kNE!kK0ikITCrA~dfI(IuDk=~qK_C!K zKva-mDiDau3PKQ-2n2!<1Oh>F*HYlZF!SCH`xbcRCA+`7nf>0(&b(O|#PM~MQQZ1~ z0y!d_K-d3`JT|jeW`OY|J9pv!nX9j!eK~t+^W^&Dg&xXmADuB5=jY~-=74F(yt5gN z0ZergzI!Wr$bVh~O3#&!0ci{S#xWe{)W4z-4d3g}dF~ZZ(l@n`@M%GtGjbADE)$okschr`W!->mBeRDR2e zWnh$ZSF%q1{`)rctvfEhu=r^6_)W>H@(1P%delz$f0#PYc6(vx~AHhLA^QeVhbk=uK!X^$*J;( z^-p1!VM?vq8#Totc3!^z_MiDj1DC(ddOmbBFH}1-sJFW>+Pkg4 Date: Sun, 9 Dec 2018 00:54:08 +0100 Subject: [PATCH 085/335] Some refactoring of net processor - introduced concept of root cluster - recursive shape iterator for clusters --- src/db/db/dbHierNetworkProcessor.cc | 232 +++++++++++++----- src/db/db/dbHierNetworkProcessor.h | 133 +++++++++- .../unit_tests/dbHierNetworkProcessorTests.cc | 6 +- testdata/algo/hc_test_au10.gds | Bin 3484 -> 3406 bytes testdata/algo/hc_test_au11.gds | Bin 15224 -> 16666 bytes testdata/algo/hc_test_au2.gds | Bin 3182 -> 2422 bytes testdata/algo/hc_test_au3.gds | Bin 3324 -> 2564 bytes testdata/algo/hc_test_au4.gds | Bin 3418 -> 2658 bytes testdata/algo/hc_test_au5.gds | Bin 2516 -> 2516 bytes testdata/algo/hc_test_au6.gds | Bin 3812 -> 3964 bytes testdata/algo/hc_test_au7.gds | Bin 5390 -> 4174 bytes testdata/algo/hc_test_au8.gds | Bin 6282 -> 4458 bytes testdata/algo/hc_test_au9.gds | Bin 2636 -> 1798 bytes 13 files changed, 313 insertions(+), 58 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 6bad4c7fb..c33724a66 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -682,8 +682,8 @@ public: /** * @brief Constructor */ - hc_receiver (const db::Layout &layout, db::connected_clusters &cell_clusters, hier_clusters &tree, const cell_clusters_box_converter &cbc, const db::Connectivity &conn) - : mp_layout (&layout), mp_tree (&tree), mp_cbc (&cbc), mp_conn (&conn) + hc_receiver (const db::Layout &layout, const db::Cell &cell, db::connected_clusters &cell_clusters, hier_clusters &tree, const cell_clusters_box_converter &cbc, const db::Connectivity &conn) + : mp_layout (&layout), mp_cell (&cell), mp_tree (&tree), mp_cbc (&cbc), mp_conn (&conn) { mp_cell_clusters = &cell_clusters; } @@ -739,6 +739,7 @@ public: private: const db::Layout *mp_layout; + const db::Cell *mp_cell; db::connected_clusters *mp_cell_clusters; hier_clusters *mp_tree; const cell_clusters_box_converter *mp_cbc; @@ -930,7 +931,7 @@ private: /** * @brief Handles a local clusters vs. the clusters of a specific child instance * @param c1 The local cluster - * @param ci2 The cell index of the child cell + * @param ci2 The cell index of the cell investigated * @param p2 The instantiation path to the child cell (last element is the instance to ci2) * @param t2 The accumulated transformation of the path */ @@ -1022,10 +1023,40 @@ private: ClusterInstance ci (id, *p); if (p == path.begin ()) { + + // if we're attaching to a child which is root yet, we need to promote the + // cluster to the parent in all places + connected_clusters &child_cc = mp_tree->clusters_per_cell (p->inst_ptr.cell_index ()); + if (child_cc.is_root (id)) { + + const db::Cell &child_cell = mp_layout->cell (p->inst_ptr.cell_index ()); + for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + + connected_clusters &parent_cc = mp_tree->clusters_per_cell (pi->parent_cell_index ()); + for (db::CellInstArray::iterator pii = pi->child_inst ().begin (); ! pii.at_end (); ++pii) { + + ClusterInstance ci2 (id, db::InstElement (pi->child_inst (), pii)); + if (mp_cell->cell_index () != pi->parent_cell_index () || ci != ci2) { + + id_type id_dummy = parent_cc.insert_dummy (); + parent_cc.add_connection (id_dummy, ci2); + + } + + } + + } + + child_cc.reset_root (id); + + } + return ci; + } - connected_clusters &target_cc = mp_tree->clusters_per_cell (p [-1].inst_ptr.cell_index ()); + db::cell_index_type pci = p [-1].inst_ptr.cell_index (); + connected_clusters &target_cc = mp_tree->clusters_per_cell (pci); id_type parent_cluster = target_cc.find_cluster_with_connection (ci); if (parent_cluster > 0) { @@ -1035,13 +1066,43 @@ private: } else { + id_type id_new = 0; + + // if we're attaching to a child which is root yet, we need to promote the + // cluster to the parent in all places + connected_clusters &child_cc = mp_tree->clusters_per_cell (p->inst_ptr.cell_index ()); + if (child_cc.is_root (id)) { + + const db::Cell &child_cell = mp_layout->cell (p->inst_ptr.cell_index ()); + for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + + connected_clusters &parent_cc = mp_tree->clusters_per_cell (pi->parent_cell_index ()); + for (db::CellInstArray::iterator pii = pi->child_inst ().begin (); ! pii.at_end (); ++pii) { + + id_type id_dummy = parent_cc.insert_dummy (); + ClusterInstance ci2 (id, db::InstElement (pi->child_inst (), pii)); + parent_cc.add_connection (id_dummy, ci2); + + if (pci == pi->parent_cell_index () && ci == ci2) { + id_new = id_dummy; + } + + } + + } + + child_cc.reset_root (id); + + } + // no parent -> create vertical connector - id = target_cc.insert_dummy (); - target_cc.add_connection (id, ci); + id = id_new; + tl_assert (id != 0); } } + } }; @@ -1165,7 +1226,7 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // NOTE: this is a receiver for both the child-to-child and // local to child interactions. - hc_receiver rec (layout, local, *this, cbc, conn); + hc_receiver rec (layout, cell, local, *this, cbc, conn); cell_inst_clusters_box_converter cibc (cbc); // The box scanner needs pointers so we have to first store the instances @@ -1241,70 +1302,131 @@ hier_clusters::clusters_per_cell (db::cell_index_type cell_index) return c->second; } -template -static -void put_or_propagate (const hier_clusters &hc, incoming_cluster_connections &inc, size_t cluster_id, const local_cluster &cluster, db::Layout &layout, db::cell_index_type ci, const std::map &lm, const db::ICplxTrans &trans) +template void insert_transformed (db::Layout &layout, db::Shapes &shapes, const Shape &s, const Trans &t); + +template void insert_transformed (db::Layout &layout, db::Shapes &shapes, const db::PolygonRef &s, const Trans &t) { - db::Cell &target_cell = layout.cell (ci); - - if (cluster_id > 0 && inc.has_incoming (ci, cluster_id)) { - - typedef std::pair reference_type; - std::map references; - - for (db::Cell::parent_inst_iterator pi = target_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { - db::Instance i = pi->child_inst (); - for (db::CellInstArray::iterator ii = i.cell_inst ().begin (); ! ii.at_end (); ++ii) { - references.insert (std::make_pair (reference_type ((*pi).parent_cell_index (), db::InstElement (i, ii)), false)); - } - } - - const typename incoming_cluster_connections::incoming_connections &connections = inc.incoming (ci, cluster_id); - for (typename incoming_cluster_connections::incoming_connections::const_iterator x = connections.begin (); x != connections.end (); ++x) { - typename std::map::iterator r = references.find (reference_type (x->parent_cell (), x->inst ())); - if (r != references.end () && ! r->second) { - put_or_propagate (hc, inc, x->parent_cluster_id (), cluster, layout, r->first.first, lm, r->first.second.complex_trans () * trans); - r->second = true; - } - } - - for (typename std::map::const_iterator r = references.begin (); r != references.end (); ++r) { - if (! r->second) { - put_or_propagate (hc, inc, 0, cluster, layout, r->first.first, lm, r->first.second.complex_trans () * trans); - } - } - - } else { - - for (typename std::map::const_iterator m = lm.begin (); m != lm.end (); ++m) { - db::Shapes shapes; - for (typename local_cluster::shape_iterator s = cluster.begin (m->first); ! s.at_end (); ++s) { - shapes.insert (*s); - } - tl::ident_map pm; - target_cell.shapes (m->second).insert_transformed (shapes, trans, pm); - } - + db::Polygon poly = s.obj (); + poly.transform (s.trans ()); + if (! t.is_unity ()) { + poly.transform (t); } + shapes.insert (db::PolygonRef (poly, layout.shape_repository ())); } template void -hier_clusters::return_to_hierarchy (db::Layout &layout, db::Cell &cell, const std::map &lm) const +hier_clusters::return_to_hierarchy (db::Layout &layout, const std::map &lm) const { - incoming_cluster_connections inc (layout, cell, *this); - for (db::Layout::bottom_up_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) { + const db::connected_clusters &cc = clusters_per_cell (*c); - for (typename db::connected_clusters::const_iterator lc = cc.begin (); lc != cc.end (); ++lc) { - put_or_propagate (*this, inc, lc->id (), *lc, layout, *c, lm, db::ICplxTrans ()); + db::Cell &target_cell = layout.cell (*c); + + for (typename db::connected_clusters::all_iterator lc = cc.begin_all (); ! lc.at_end (); ++lc) { + + if (cc.is_root (*lc)) { + + for (typename std::map::const_iterator m = lm.begin (); m != lm.end (); ++m) { + + db::Shapes &shapes = target_cell.shapes (m->second); + + for (recursive_cluster_shape_iterator si (*this, m->first, *c, *lc); ! si.at_end (); ++si) { + insert_transformed (layout, shapes, *si, si.trans ()); + } + + } + + } + } + } } // explicit instantiations template class DB_PUBLIC hier_clusters; +// ------------------------------------------------------------------------------ +// recursive_cluster_shape_iterator implementation + +template +recursive_cluster_shape_iterator::recursive_cluster_shape_iterator (const hier_clusters &hc, unsigned int layer, db::cell_index_type ci, typename local_cluster::id_type id) + : mp_hc (&hc), m_layer (layer), m_id (id) +{ + down (ci, id, db::ICplxTrans ()); + + while (m_shape_iter.at_end () && ! m_conn_iter_stack.empty ()) { + next_conn (); + } +} + +template +recursive_cluster_shape_iterator &recursive_cluster_shape_iterator::operator++ () +{ + ++m_shape_iter; + + while (m_shape_iter.at_end () && ! m_conn_iter_stack.empty ()) { + next_conn (); + } + + return *this; +} + +template +void recursive_cluster_shape_iterator::next_conn () +{ + if (m_conn_iter_stack.back ().first != m_conn_iter_stack.back ().second) { + + const ClusterInstance &cli = *m_conn_iter_stack.back ().first; + down (cli.inst ().inst_ptr.cell_index (), cli.id (), cli.inst ().complex_trans ()); + + } else { + + while (m_conn_iter_stack.back ().first == m_conn_iter_stack.back ().second) { + + up (); + if (m_conn_iter_stack.empty ()) { + return; + } + + ++m_conn_iter_stack.back ().first; + + } + + } +} + +template +void recursive_cluster_shape_iterator::up () +{ + m_conn_iter_stack.pop_back (); + m_trans_stack.pop_back (); + m_cell_index_stack.pop_back (); +} + +template +void recursive_cluster_shape_iterator::down (db::cell_index_type ci, typename db::local_cluster::id_type id, const db::ICplxTrans &t) +{ + const connected_clusters &clusters = mp_hc->clusters_per_cell (ci); + const typename connected_clusters::connections_type &conn = clusters.connections_for_cluster (id); + + if (! m_trans_stack.empty ()) { + m_trans_stack.push_back (m_trans_stack.back () * t); + } else { + m_trans_stack.push_back (t); + } + + m_cell_index_stack.push_back (ci); + m_conn_iter_stack.push_back (std::make_pair (conn.begin (), conn.end ())); + + const local_cluster &cluster = mp_hc->clusters_per_cell (cell_index ()).cluster_by_id (cluster_id ()); + m_shape_iter = cluster.begin (m_layer); +} + +// explicit instantiations +template class DB_PUBLIC recursive_cluster_shape_iterator; + // ------------------------------------------------------------------------------ // incoming_cluster_connections implementation diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index e102a5d26..2442298cb 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -229,6 +229,7 @@ template class DB_PUBLIC local_clusters { public: + typedef typename local_cluster::id_type id_type; typedef typename local_cluster::box_type box_type; typedef db::box_tree, local_cluster_box_convert > tree_type; typedef typename tree_type::touching_iterator touching_iterator; @@ -372,6 +373,14 @@ public: return m_id == other.m_id && m_inst == other.m_inst; } + /** + * @brief Inequality + */ + bool operator!= (const ClusterInstance &other) const + { + return ! operator== (other); + } + /** * @brief Less operator */ @@ -434,12 +443,20 @@ private: /** * @brief Local clusters with connections to clusters from child cells + * + * Clusters can get connected. There are incoming connections (from above the hierarchy) + * and outgoing connections (down to a child cell). + * + * "root" clusters are some that don't have incoming connections. There are only + * root clusters or clusters which are connected from every parent cell. There are no + * "half connected" clusters. */ template class DB_PUBLIC connected_clusters : public local_clusters { public: + typedef typename local_clusters::id_type id_type; typedef std::list connections_type; typedef typename local_clusters::box_type box_type; typedef connected_clusters_iterator all_iterator; @@ -507,11 +524,29 @@ public: return m_connections.end (); } + /** + * @brief Returns true, if the given cluster ID is a root cluster + */ + bool is_root (id_type id) const + { + return m_connected_clusters.find (id) == m_connected_clusters.end (); + } + + /** + * @brief Resets the root status of a cluster + * CAUTION: don't call this method unless you know what you're doing. + */ + void reset_root (id_type id) + { + m_connected_clusters.insert (id); + } + private: template friend class connected_clusters_iterator; - std::map::id_type, connections_type> m_connections; + std::map m_connections; std::map::id_type> m_rev_connections; + std::set m_connected_clusters; }; template @@ -573,7 +608,7 @@ public: * The backannotation process usually involves propagation of shapes up in the hierarchy * to resolve variants. */ - void return_to_hierarchy (db::Layout &layout, db::Cell &cell, const std::map &lm) const; + void return_to_hierarchy (db::Layout &layout, const std::map &lm) const; /** * @brief Clears this collection @@ -589,6 +624,100 @@ private: std::map > m_per_cell_clusters; }; +/** + * @brief A recursive shape iterator for the shapes of a cluster + * + * This iterator will deliver the shapes of a cluster including the shapes for the + * connected child clusters. + * + * This iterator applies to one layer. + */ +template +class DB_PUBLIC recursive_cluster_shape_iterator +{ +public: + typedef T value_type; + typedef const T &reference; + typedef const T *pointer; + + /** + * @brief Constructor + */ + recursive_cluster_shape_iterator (const hier_clusters &hc, unsigned int layer, db::cell_index_type ci, typename local_cluster::id_type id); + + /** + * @brief Returns a value indicating whether there are any more shapes + */ + bool at_end () const + { + return m_shape_iter.at_end (); + } + + /** + * @brief Returns the shape (untransformed) + */ + reference operator* () const + { + return *m_shape_iter; + } + + /** + * @brief Returns the shape pointer (untransformed) + */ + pointer operator-> () const + { + return m_shape_iter.operator-> (); + } + + /** + * @brief Returns the transformation applicable for transforming the shape to the root cluster + */ + const db::ICplxTrans &trans () const + { + return m_trans_stack.back (); + } + + /** + * @brief Returns the cell index the shape lives in + */ + db::cell_index_type cell_index () const + { + return m_cell_index_stack.back (); + } + + /** + * @brief Returns the id of the current cluster + */ + typename db::local_cluster::id_type cluster_id () const + { + if (m_conn_iter_stack.size () <= 1) { + return m_id; + } else { + return m_conn_iter_stack [m_conn_iter_stack.size () - 2].first->id (); + } + } + + /** + * @brief Increment operator + */ + recursive_cluster_shape_iterator &operator++ (); + +private: + typedef typename db::connected_clusters::connections_type connections_type; + + const hier_clusters *mp_hc; + std::vector m_trans_stack; + std::vector m_cell_index_stack; + std::vector > m_conn_iter_stack; + typename db::local_cluster::shape_iterator m_shape_iter; + unsigned int m_layer; + typename db::local_cluster::id_type m_id; + + void next_conn (); + void up (); + void down (db::cell_index_type ci, typename db::local_cluster::id_type id, const db::ICplxTrans &t); +}; + /** * @brief A connection to a cluster from a parent cluster */ diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index d527e4b7d..90bc2701d 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -515,6 +515,10 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std const db::connected_clusters &clusters = hc.clusters_per_cell (*td); for (db::connected_clusters::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { + if (! clusters.is_root (*c)) { + continue; + } + net_layers.push_back (std::make_pair (0, ly.insert_layer ())); unsigned int lout = net_layers.back ().second; @@ -601,7 +605,7 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str lm[l1] = ly.insert_layer (db::LayerProperties (101, 0)); lm[l2] = ly.insert_layer (db::LayerProperties (102, 0)); lm[l3] = ly.insert_layer (db::LayerProperties (103, 0)); - hc.return_to_hierarchy (ly, ly.cell (*ly.begin_top_down ()), lm); + hc.return_to_hierarchy (ly, lm); CHECKPOINT(); db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/" + au_file); diff --git a/testdata/algo/hc_test_au10.gds b/testdata/algo/hc_test_au10.gds index d7c92fb935d018da10d349f0310b76a1a850054f..66499fd67d8879d97fbdace91b5088c7905ab957 100644 GIT binary patch delta 631 zcmaixy-Nad7{|YNzMP~&CT!%2lJgfsz99w=W4#cQfZYqS@z--sGEkHUOz1D8jAif`B) zn0D&HilNe8OPyL_=X@w_I2mi+qwc)AaXv%TDY^V?0);A(P<*GldJmy^zG3VKs$T7= delta 328 zcmX>nHAh;BfsKKQDS|VI%I=wjkDYOP<6;2^58?zZgv_3R4 zy}$G8@0^=^Q$K$8L#G!4;Pd67+wlc|hA6ZEz%$VD=-;P$`;z|^kH6d#1n6#lY~fn`8CaRAbF*lxX7!(Q1@;0j_z+YI7WYZcEM5Chx~}OPc^qCy1`q ztv1I*-Bu zdC6EaX18m8!g#~joy_4^i5O0{t6v!F#+N!YpELes?C;e4sIjarM!Syf9Iks)2Pa$L z?$c`EGjPVL09KnfrLmOm8f>V`WdNFI!4W&6T=#*e=2#d=HP#G=0GxZ^h`}S^j3hiY z$Jf(aYWD5}NS*{o>{{XfSM$^y5AH|}|Mh53hvu{=QF@u?J8hMo^t#(qL*3LK?MalL zri-4^)6^cfr-qvGPVGsQo(`uyrKj;ux2J}h$-MR?N>5Y&cuG%`dFkoY=aGJ*@_C~B zCH3zbKOKG|tIhA1q!(VFA-cRw^at07b^f+~Tk+H!7k10uy!O%=eo8{>pKCNmmx-)4ui@SB4n4MP{eYKTI>Y0cBf7%3Pqn$|9s9z5eaJ8D zms=;tx7u7Dex&BuQ@^h0L`0cHt<6&=qSs|>S>Ah#A602wl!5%V?y0#9#9g{Juk#RP z4(ohytIT6<9PV`)hkIn%P^PgquT!|gH(sW3CvS5*4fhtWv^Wic4v^|Eiv7GC& zF%j3#6UKStJ>%#$y)p&&;<^ce!=D5(CGK=&4wutQmN8DZp=OB1$j+eXL#r@#CSq5l5t#-_S z^ETfttIbmqA#S>%NGEO*7mFUupzgL^}cf77U)-cL?$q$SD6@Ol8Z6v z|7X&&?SWFm7TroJ<)Jac=LF2cGK_`z_cSa(U)ahe9kPv9!zPtfovxrGQk@NoM`E@9 zxHjwb21BEKe+%A0Z`7n|L#IWRdkVkcq{jPQ_zb&s-lt$58iq+TBPuOLt(q6`8%~Y! zehuJv&B8K_HSsj%l<5 bAE967{TjT6PKEbzcmhjGSgHH~lEi)hOOfCr diff --git a/testdata/algo/hc_test_au2.gds b/testdata/algo/hc_test_au2.gds index 86ba92af2e58005b024b12fee6646d1c2bc99925..73576e4f40883305ee0ec8b4f929dcb762ec2a79 100644 GIT binary patch delta 114 zcmaDS@l8mHfsKKQDS|=WfHGx9PlfQsz)yZZ|U?r2wfOI081mo+8uZ$s#$y$ss)+z*x u$ylFFn~8yuflY_a*^q%nkb!}Poq<7y6{j0FF>A6hzL~7UVmvvPQxX7*Wh?yv diff --git a/testdata/algo/hc_test_au3.gds b/testdata/algo/hc_test_au3.gds index c1998d1e48d9d6630451916beead0e3003bd2a3e..54b1e4e9511a066a2dbeb80564c44ae571341f68 100644 GIT binary patch delta 110 zcmew(*&?FEz{bGD6u}_F$i)7Lfro*EL5M+%K^~bsQAt%8i}1#%4kkHv1_l{coH{rE QVBO9(`7AT{Y08eQVssI20 delta 215 zcmZn>`6H>sz{bGD6u}_F$i)7Lfro*GL6m`=!3dc>QAt%8i}1#%4yMT)I7KFwsZXBI zsXB3+`ebDe3C5R`vlxwm0?L!WFlvG%CYv#V6-+Jz()R2UjISoXGKMfFYXOylg(j~; wu$YYX*|eD$7#Y}f*qjX+SOgguSlAgDWLR;!a1--(HpbVJb6AWgzvh$#09Pk3J^%m! diff --git a/testdata/algo/hc_test_au4.gds b/testdata/algo/hc_test_au4.gds index 57d1aa7c0b610f89035a3f55f25b1bdc0a53555e..37e140f06d0ac1820ca041cc9ceb6dcff0670972 100644 GIT binary patch delta 102 zcmca5^+-gCfsKKQDS|B(h4x{ym>%)7rGypBA0zfu2f%v!#5H4`W&Jmw$0D{7Y3uTY_yR6j! zFaePm%OeiAB!xF&tn6V2Kz9*|kpbe1D=ED0j+I@9SS*D|H0y|+oTTuPl(pzh%>x8# zd@Ldbr=yZIE`J||X~lMmpMX}Ms`3*Mg>Q?_+A3nR0zh=40LGf6@XgAov%b%dN_^Yp z^9$0^`Fq)uPCGZ$hJB2KJ;b|hN#UI+C2v<)s)$JJULn3WB!zd1lB(h4nvqp`;up=ysr-VI<5-0zn*fyng(hzS)2frV zF{$gbX)`e}GO+2eIU6#t2r@9Rurn~ou;O&vB<8u3WqElwPvX&I+P*z@Z_6Jbs9VhQg(f z&`?bc4Gj{}+y@Biyq;cefp*~D-{rZ_dCzmsd(O*`*&Hkw#tt$*!+u2w4&sPm<@fhR zt`Hw1-|U_mc^k->6KBG*tc{xN*hv|;vhu<276TuE-2#ZgTOhLLk#)P(6Zu8ua{#lg z88`z%9X0QVteb7^Ra<~)07Pf?@58!iDDMJ*JHT4k3?HgB5&)BROKwNfqr~B;u1s`3 zf#lF5Ro4G?ig;dCRf9+gQ~MrS2jr$bJMKi~vAtI;3BXqYh~gTkH9fNKtMtT-{Bqo4 z^%C%R0b+7mxz#F6*8N>}&#S|uL8N%)zDL#pc9-sfm~s$PPr$O;43qU(pWM}eTcm7M zlNf&1nVGCpvqtU$WiwxNc4`-8X0mR|lW2NWo3goedIO0qy$+Lgx6Vl@8(k$LHHR`Y nS+`pgn{X7&&HaC^<$LHrE+yPSe(KGT!)RjAbgzV^CJBLWY*Kg^ delta 1246 zcmbu8zfTlF6vw|iyX>9b9(OFi$qIU~dx&;-l~V90P*4yISmB` z1u@1@XrQ#X!qQmT(GfxnMzo-~lKIX!4i}CBZ1ywzeecb@@5{`$`)}qd2qDBW5>Mog z5Fx-Q#xZqy-KwO6J>*Z%HifMFajWrAzVxSC2Sh4gc~!-#?06|$d3N=|twc@!43b_4zV)Bh&Jgws0FTQ)FgOE5iQ({TVB|euY4s?PFZ}WDI`J8Z z+Wt9sp3dn`CwJvx@B!M$F# z$SY~PU(!4zD%d)}}P+kW}PNV$Q(dy-Oom$#4gCH*tk7XQ$iR)DU zMol9R9VqXSn%iGmJ!(4^TpBX109`;HPah8q0)noj%>ojsmTv$mn7Y@H%q!V@oVqKNv`rFuWnRP_`h%YG`i|;@HD#WZtyg^ i>XiBac^X-MLmn22``d-RK>oQqvi~lBB*L5;VSo!?X4NkM diff --git a/testdata/algo/hc_test_au8.gds b/testdata/algo/hc_test_au8.gds index 4cd9a66a5347520e55f6814f4c47268b5c712805..587bff467e7ce81473259903deb52d0a21ae39ac 100644 GIT binary patch delta 665 zcmaKqKTE?v7>9qkT<=2v#MBsDp%sZWP${UzQKYNlDu|1VV+RKZaq7@P#Km4efRhwl zlq`M(5#9U%BAuN403zONJfqNh5T4`VckjLLk<*peFoXbOJE&Mpz90_=J|m_B0&N~o1 zr=+s3_fyu_4bvi0bESin#sg|oR!2RiI+ok&Dp!c=9IzSykzWJN{glRAjUJy-dpf-| z0PHS649YsKZ!$FA?wa(xemh!3YH_ij(s;n6>kmLtCy1pdU|A1jXne6hcA(3AIQ#$K Y5l_4KEWQYaOm)0+JoE-!=o6gd6I6j=XaE2J literal 6282 zcmbuDKWr685XQ&vpC8V{7-NEEo)RYk#S)eh0#Y0aMTAg*Vks$HAVlGUf`Wp=g$oxh zC`d=)B2p+^L<&U;3KtY9T%d@8B83YVgdqO`#~5RR0TT?H@4LCl-s$m@iEQcVXU*Pz zw>Pspv%6A}LgAFuTMDgnQj?PGk#{Bczf=pA!IQ^Cx{Bq`zxejkuYUP)?X%<8Kbrjh z^pMocxo5K)QmItFJSe5ANM(CLTBOom5Rv|lVhEde?I+UGpZ%Ggl+sRlbN_H9gx9qY z!WEHXuSmJeXm+xB|ED7%JgJ93ZjObpu@PzZC-vt2pWj0N@es(Tvm)DuBhCI~JlDSv zzaL{=f+x-@OM~ct10R~w>|pl$GyOwYKM}%TDR-hquI%3{Y6@0yk3$S!0NK11#v6M6e;q}joS(IN4mDim{Cg`Y(_r;&-k-Q6fS-$a@nbZK>-!3%zX z9=IJ@X3tEd*+G{Uy-{08d?LFpLH8)WH>KIZ1}#SfP#@nAfz$dfNg}X+9QpaWM&O@k zlNMO{ec3FxnvD%BYk5?wYx^uR^)(Tr*};Z!r?JUt6)$ooB13`I!x|T(*+G{Uzi#^p z{YJ)^V=?Y4Q2mxc2Lsg1>6;eM+-~F0Gbz_eGF&rOhcF?7T-A$rz{YF0JqHZPm4{zdMHvc`!&Vw!1rkvImYg0~Zi?u1I zMF((h^UrFM(d^*=^>?*W8TuNjIaVil9Q*v4q?#liZ$lyMteiZQ**WCu*0$lKQe>Y! z$hd_tHxhbsLs(z!<>^Mcc#`SEHCsxc4@Og=|JJRep-MF-P%dm+!;+7#f32#go%}#o> zm|u0ylDIXIHu&F|oY1x1F)qCsxHq6&zR22#_@y*E=+Y{8VIH8akgMl0uD(dKgD$P& zQV0)^!bc!|*7|Ft*+G{UZx7anxu0b`xnGPlJLuBlcz$Q>!24&p?}#)z=+M%elRG8l z9Fi~nW;FYql27ZwAo2pfk9^#x`Os+gmFD~P+>b;m&UDE8!y}ofRTvZ>D92aw|ku^ixeOk=J^bff)p*h-U zcFbYdoOn5JME0ISAMhQl%U8!E&A#`PPm8y{%<*LXBI{A4+1u89TAWusm-Y6vx2o|p fn!U#rNBDeJYa-b`0^g5CvQ}z)j(#EI6z{bGD6u}_F$i)7Lfro*EL6|{^K@*uhQAt%8n{bqu96JMp3@c8Jn*|s< Mm{>xntW zhHTnQ42%pMI&9AVetrxrf(#5S> Date: Sun, 9 Dec 2018 23:24:32 +0100 Subject: [PATCH 086/335] WIP: property collection in net clusters, more tests. --- src/db/db/dbHierNetworkProcessor.cc | 126 +++++- src/db/db/dbHierNetworkProcessor.h | 104 +++++ .../unit_tests/dbHierNetworkProcessorTests.cc | 369 ++++++++++++++++-- testdata/algo/hc_test_au1.gds | Bin 1526 -> 1604 bytes testdata/algo/hc_test_au1b.gds | Bin 1386 -> 1448 bytes testdata/algo/hc_test_au9.gds | Bin 1798 -> 2020 bytes testdata/algo/hc_test_au9b.gds | Bin 1664 -> 1854 bytes testdata/algo/hc_test_l1.gds | Bin 746 -> 808 bytes testdata/algo/hc_test_l9.gds | Bin 960 -> 1150 bytes 9 files changed, 554 insertions(+), 45 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index c33724a66..2793eb707 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -156,6 +156,16 @@ local_cluster::clear () m_shapes.clear (); m_needs_update = false; m_bbox = box_type (); + m_attrs.clear (); +} + +template +void +local_cluster::add_attr (attr_id a) +{ + if (a > 0) { + m_attrs.insert (a); + } } template @@ -175,6 +185,8 @@ local_cluster::join_with (const local_cluster &other) other_tree.insert (s->second.begin (), s->second.end ()); } + m_attrs.insert (other.m_attrs.begin (), other.m_attrs.end ()); + m_needs_update = true; } @@ -427,7 +439,7 @@ namespace template struct cluster_building_receiver - : public db::box_scanner_receiver + : public db::box_scanner_receiver > { typedef typename local_cluster::id_type id_type; @@ -437,9 +449,9 @@ struct cluster_building_receiver // .. nothing yet.. } - void add (const T *s1, unsigned int l1, const T *s2, unsigned int l2) + void add (const T *s1, std::pair p1, const T *s2, std::pair p2) { - if (! mp_conn->interacts (*s1, l1, *s2, l2)) { + if (! mp_conn->interacts (*s1, p1.first, *s2, p2.first)) { return; } @@ -451,8 +463,10 @@ struct cluster_building_receiver if (id2 == m_shape_to_cluster_id.end ()) { local_cluster *cluster = mp_clusters->insert (); - cluster->add (*s1, l1); - cluster->add (*s2, l2); + cluster->add (*s1, p1.first); + cluster->add (*s2, p2.first); + cluster->add_attr (p1.second); + cluster->add_attr (p2.second); m_shape_to_cluster_id.insert (std::make_pair (s1, cluster->id ())); m_shape_to_cluster_id.insert (std::make_pair (s2, cluster->id ())); @@ -460,7 +474,9 @@ struct cluster_building_receiver } else { // NOTE: const_cast is in order - we know what we're doing. - const_cast &> (mp_clusters->cluster_by_id (id2->second)).add (*s1, l1); + local_cluster &c2 = const_cast &> (mp_clusters->cluster_by_id (id2->second)); + c2.add (*s1, p1.first); + c2.add_attr (p1.second); m_shape_to_cluster_id.insert (std::make_pair (s1, id2->second)); } @@ -468,7 +484,9 @@ struct cluster_building_receiver } else if (id2 == m_shape_to_cluster_id.end ()) { // NOTE: const_cast is in order - we know what we're doing. - const_cast &> (mp_clusters->cluster_by_id (id1->second)).add (*s2, l2); + local_cluster &c1 = const_cast &> (mp_clusters->cluster_by_id (id1->second)); + c1.add (*s2, p2.first); + c1.add_attr (p2.second); m_shape_to_cluster_id.insert (std::make_pair (s2, id1->second)); } else if (id1->second != id2->second) { @@ -481,13 +499,14 @@ struct cluster_building_receiver } } - void finish (const T *s, unsigned int l) + void finish (const T *s, std::pair p) { // if the shape has not been handled yet, insert a single cluster with only this shape typename std::map::const_iterator id = m_shape_to_cluster_id.find (s); if (id == m_shape_to_cluster_id.end ()) { local_cluster *cluster = mp_clusters->insert (); - cluster->add (*s, l); + cluster->add (*s, p.first); + cluster->add_attr (p.second); } } @@ -503,14 +522,14 @@ template void local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) { - db::box_scanner bs; + db::box_scanner > bs; typename T::tag object_tag; db::box_convert bc; for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { const db::Shapes &shapes = cell.shapes (*l); for (db::Shapes::shape_iterator s = shapes.begin (shape_flags); ! s.at_end (); ++s) { - bs.insert (s->basic_ptr (object_tag), *l); + bs.insert (s->basic_ptr (object_tag), std::make_pair (*l, (attr_id) s->prop_id ())); } } @@ -1361,6 +1380,19 @@ recursive_cluster_shape_iterator::recursive_cluster_shape_iterator (const hie } } +template +std::vector recursive_cluster_shape_iterator::inst_path () const +{ + std::vector p; + if (!m_conn_iter_stack.empty ()) { + p.reserve (m_conn_iter_stack.size ()); + for (size_t i = 0; i < m_conn_iter_stack.size () - 1; ++i) { + p.push_back (*m_conn_iter_stack [i].first); + } + } + return p; +} + template recursive_cluster_shape_iterator &recursive_cluster_shape_iterator::operator++ () { @@ -1427,6 +1459,78 @@ void recursive_cluster_shape_iterator::down (db::cell_index_type ci, typename // explicit instantiations template class DB_PUBLIC recursive_cluster_shape_iterator; +// ------------------------------------------------------------------------------ +// recursive_cluster_iterator implementation + +template +recursive_cluster_iterator::recursive_cluster_iterator (const hier_clusters &hc, db::cell_index_type ci, typename local_cluster::id_type id) + : mp_hc (&hc), m_id (id) +{ + down (ci, id); +} + +template +std::vector recursive_cluster_iterator::inst_path () const +{ + std::vector p; + if (!m_conn_iter_stack.empty ()) { + p.reserve (m_conn_iter_stack.size ()); + for (size_t i = 0; i < m_conn_iter_stack.size () - 1; ++i) { + p.push_back (*m_conn_iter_stack [i].first); + } + } + return p; +} + +template +recursive_cluster_iterator &recursive_cluster_iterator::operator++ () +{ + next_conn (); + return *this; +} + +template +void recursive_cluster_iterator::next_conn () +{ + while (m_conn_iter_stack.back ().first == m_conn_iter_stack.back ().second) { + + up (); + if (m_conn_iter_stack.empty ()) { + return; + } + + ++m_conn_iter_stack.back ().first; + + } + + if (m_conn_iter_stack.back ().first != m_conn_iter_stack.back ().second) { + + const ClusterInstance &cli = *m_conn_iter_stack.back ().first; + down (cli.inst ().inst_ptr.cell_index (), cli.id ()); + + } +} + +template +void recursive_cluster_iterator::up () +{ + m_conn_iter_stack.pop_back (); + m_cell_index_stack.pop_back (); +} + +template +void recursive_cluster_iterator::down (db::cell_index_type ci, typename db::local_cluster::id_type id) +{ + const connected_clusters &clusters = mp_hc->clusters_per_cell (ci); + const typename connected_clusters::connections_type &conn = clusters.connections_for_cluster (id); + + m_cell_index_stack.push_back (ci); + m_conn_iter_stack.push_back (std::make_pair (conn.begin (), conn.end ())); +} + +// explicit instantiations +template class DB_PUBLIC recursive_cluster_iterator; + // ------------------------------------------------------------------------------ // incoming_cluster_connections implementation diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 2442298cb..d3afe5412 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -123,6 +123,9 @@ public: typedef typename T::box_type box_type; typedef db::unstable_box_tree > tree_type; typedef typename tree_type::flat_iterator shape_iterator; + typedef unsigned int attr_id; + typedef std::set attr_set; + typedef attr_set::const_iterator attr_iterator; /** * @brief Creates an empty cluster @@ -179,6 +182,31 @@ public: */ shape_iterator begin (unsigned int l) const; + /** + * @brief Adds the given attribute to the attribute set + * + * Attributes are arbitary IDs attached to clusters. + * The attribute value 0 is reserved for "no attribute" and is not + * put into the set. + */ + void add_attr (attr_id a); + + /** + * @brief Gets the attribute iterator (begin) + */ + attr_iterator begin_attr () const + { + return m_attrs.begin (); + } + + /** + * @brief Gets the attribute iterator (end) + */ + attr_iterator end_attr () const + { + return m_attrs.end (); + } + private: template friend class local_clusters; template friend class interaction_receiver; @@ -201,6 +229,7 @@ private: bool m_needs_update; std::map m_shapes; box_type m_bbox; + attr_set m_attrs; }; /** @@ -231,6 +260,7 @@ class DB_PUBLIC local_clusters public: typedef typename local_cluster::id_type id_type; typedef typename local_cluster::box_type box_type; + typedef typename local_cluster::attr_id attr_id; typedef db::box_tree, local_cluster_box_convert > tree_type; typedef typename tree_type::touching_iterator touching_iterator; typedef typename tree_type::const_iterator const_iterator; @@ -669,6 +699,13 @@ public: return m_shape_iter.operator-> (); } + /** + * @brief Returns the instantiation path of the current cluster + * + * The call path's root is the initial cell + */ + std::vector inst_path () const; + /** * @brief Returns the transformation applicable for transforming the shape to the root cluster */ @@ -718,6 +755,73 @@ private: void down (db::cell_index_type ci, typename db::local_cluster::id_type id, const db::ICplxTrans &t); }; +/** + * @brief A recursive cluster iterator for the clusters itself + * + * This iterator will deliver the child clusters of a specific cluster. + */ +template +class DB_PUBLIC recursive_cluster_iterator +{ +public: + /** + * @brief Constructor + */ + recursive_cluster_iterator (const hier_clusters &hc, db::cell_index_type ci, typename local_cluster::id_type id); + + /** + * @brief Returns a value indicating whether there are any more shapes + */ + bool at_end () const + { + return m_cell_index_stack.empty (); + } + + /** + * @brief Returns the cell index the shape lives in + */ + db::cell_index_type cell_index () const + { + return m_cell_index_stack.back (); + } + + /** + * @brief Returns the id of the current cluster + */ + typename db::local_cluster::id_type cluster_id () const + { + if (m_conn_iter_stack.size () <= 1) { + return m_id; + } else { + return m_conn_iter_stack [m_conn_iter_stack.size () - 2].first->id (); + } + } + + /** + * @brief Returns the instantiation path of the current cluster + * + * The call path's root is the initial cell + */ + std::vector inst_path () const; + + /** + * @brief Increment operator + */ + recursive_cluster_iterator &operator++ (); + +private: + typedef typename db::connected_clusters::connections_type connections_type; + + const hier_clusters *mp_hc; + std::vector m_cell_index_stack; + std::vector > m_conn_iter_stack; + typename db::local_cluster::id_type m_id; + + void next_conn (); + void up (); + void down (db::cell_index_type ci, typename db::local_cluster::id_type id); +}; + /** * @brief A connection to a cluster from a parent cluster */ diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 90bc2701d..0fba6ec8e 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -149,13 +149,21 @@ TEST(10_LocalClusterBasic) EXPECT_EQ (cluster.id (), size_t (0)); cluster.add (db::PolygonRef (poly, repo), 0); + cluster.add_attr (1); EXPECT_EQ (cluster.bbox ().to_string (), "(0,0;1000,1000)"); db::local_cluster cluster2; - cluster.add (db::PolygonRef (poly, repo).transformed (db::Trans (db::Vector (10, 20))), 1); + cluster2.add (db::PolygonRef (poly, repo).transformed (db::Trans (db::Vector (10, 20))), 1); + cluster2.add_attr (2); cluster.join_with (cluster2); EXPECT_EQ (cluster.bbox ().to_string (), "(0,0;1010,1020)"); + + EXPECT_EQ (cluster.begin_attr () == cluster.end_attr (), false); + db::local_cluster::attr_iterator a = cluster.begin_attr (); + EXPECT_EQ (*a++, 1u); + EXPECT_EQ (*a++, 2u); + EXPECT_EQ (a == cluster.end_attr (), true); } TEST(11_LocalClusterInteractBasic) @@ -248,6 +256,9 @@ static std::string local_cluster_to_string (const db::local_cluster &cluster, res += "[" + tl::to_string (*l) + "]" + obj2string (*s); } } + for (typename db::local_cluster::attr_iterator a = cluster.begin_attr (); a != cluster.end_attr (); ++a) { + res += "%" + tl::to_string (*a); + } return res; } @@ -325,6 +336,67 @@ TEST(20_LocalClustersBasic) ); } +TEST(21_LocalClustersBasicWithAttributes) +{ + db::Layout layout; + db::Cell &cell = layout.cell (layout.add_cell ("TOP")); + db::GenericRepository &repo = layout.shape_repository (); + + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + conn.connect (0, 1); + conn.connect (0, 2); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + cell.shapes (0).insert (db::PolygonRef (poly, repo)); + + db::local_clusters clusters; + EXPECT_EQ (local_clusters_to_string (clusters, conn), ""); + + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0)"); + + // one more shape + cell.shapes (0).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (10, 20))), repo), 1)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1"); + + // one more shape creating a new cluster + cell.shapes (2).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1100))), repo), 2)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1\n" + "#2:[2](0,1100;0,2100;1000,2100;1000,1100)%2" + ); + + // one more shape connecting these + cell.shapes (2).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1000))), repo), 3)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20);[2](0,1000;0,2000;1000,2000;1000,1000);[2](0,1100;0,2100;1000,2100;1000,1100)%1%2%3" + ); + + // one more shape opening a new cluster + cell.shapes (1).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1100))), repo), 4)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20);[2](0,1000;0,2000;1000,2000;1000,1000);[2](0,1100;0,2100;1000,2100;1000,1100)%1%2%3\n" + "#2:[1](0,1100;0,2100;1000,2100;1000,1100)%4" + ); +} + TEST(30_LocalConnectedClusters) { db::Layout layout; @@ -408,28 +480,234 @@ TEST(30_LocalConnectedClusters) EXPECT_EQ (x.size (), size_t (0)); } -static void normalize_layer (db::Layout &layout, unsigned int layer) +static db::PolygonRef make_box (db::Layout &ly, const db::Box &box) { - for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { - db::Shapes s (layout.is_editable ()); - s.swap (c->shapes (layer)); - for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); !i.at_end (); ++i) { - db::Polygon poly; - i->polygon (poly); - c->shapes (layer).insert (db::PolygonRef (poly, layout.shape_repository ())); - } - } + return db::PolygonRef (db::Polygon (box), ly.shape_repository ()); } -static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const db::hier_clusters &hc, db::local_cluster::id_type cluster_id, const db::ICplxTrans &trans, const db::Connectivity &conn) +TEST(40_HierClustersBasic) +{ + db::hier_clusters hc; + + db::Layout ly; + unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0)); + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + top.shapes (l1).insert (make_box (ly, db::Box (0, 0, 1000, 1000))); + + db::Cell &c1 = ly.cell (ly.add_cell ("C1")); + c1.shapes (l1).insert (make_box (ly, db::Box (0, 0, 2000, 500))); + top.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans ())); + + db::Cell &c2 = ly.cell (ly.add_cell ("C2")); + c2.shapes (l1).insert (make_box (ly, db::Box (0, 0, 500, 2000))); + c2.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans ())); + top.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans ())); + + db::Connectivity conn; + conn.connect (l1, l1); + + hc.build (ly, top, db::ShapeIterator::Polygons, conn); + + int n, nc; + const db::connected_clusters *cluster; + + // 1 cluster in TOP with 2 connections + n = 0; + cluster = &hc.clusters_per_cell (top.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (cluster->bbox ().to_string (), "(0,0;1000,1000)") + nc = 0; + for (db::connected_clusters::connections_iterator i = cluster->begin_connections (); i != cluster->end_connections (); ++i) { + nc += i->second.size (); + } + EXPECT_EQ (nc, 2); + + // 1 cluster in C1 without connection + n = 0; + cluster = &hc.clusters_per_cell (c1.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (cluster->bbox ().to_string (), "(0,0;2000,500)") + nc = 0; + for (db::connected_clusters::connections_iterator i = cluster->begin_connections (); i != cluster->end_connections (); ++i) { + nc += i->second.size (); + } + EXPECT_EQ (nc, 0); + + // 1 cluster in C2 with one connection + n = 0; + cluster = &hc.clusters_per_cell (c2.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (cluster->bbox ().to_string (), "(0,0;500,2000)") + nc = 0; + for (db::connected_clusters::connections_iterator i = cluster->begin_connections (); i != cluster->end_connections (); ++i) { + nc += i->second.size (); + } + EXPECT_EQ (nc, 1); +} + +static std::string path2string (const db::Layout &ly, db::cell_index_type ci, const std::vector &path) +{ + std::string res = ly.cell_name (ci); + for (std::vector::const_iterator p = path.begin (); p != path.end (); ++p) { + res += "/"; + res += ly.cell_name (p->inst ().inst_ptr.cell_index ()); + } + return res; +} + +static std::string rcsiter2string (const db::Layout &ly, db::cell_index_type ci, db::recursive_cluster_shape_iterator si) +{ + std::string res; + while (! si.at_end ()) { + db::Polygon poly = si->obj (); + poly.transform (si->trans ()); + poly.transform (si.trans ()); + if (! res.empty ()) { + res += ";"; + } + res += path2string (ly, ci, si.inst_path ()); + res += ":"; + res += poly.to_string (); + ++si; + } + return res; +} + +static std::string rciter2string (const db::Layout &ly, db::cell_index_type ci, db::recursive_cluster_iterator si) +{ + std::string res; + while (! si.at_end ()) { + if (! res.empty ()) { + res += ";"; + } + res += path2string (ly, ci, si.inst_path ()); + ++si; + } + return res; +} + +TEST(41_HierClustersRecursiveClusterShapeIterator) +{ + db::hier_clusters hc; + + db::Layout ly; + unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0)); + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + top.shapes (l1).insert (make_box (ly, db::Box (0, 0, 1000, 1000))); + + db::Cell &c1 = ly.cell (ly.add_cell ("C1")); + c1.shapes (l1).insert (make_box (ly, db::Box (0, 0, 2000, 500))); + top.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 10)))); + + db::Cell &c2 = ly.cell (ly.add_cell ("C2")); + c2.shapes (l1).insert (make_box (ly, db::Box (0, 0, 500, 2000))); + c2.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 20)))); + top.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans (db::Vector (0, 30)))); + + db::Connectivity conn; + conn.connect (l1, l1); + + hc.build (ly, top, db::ShapeIterator::Polygons, conn); + + std::string res; + int n = 0; + db::connected_clusters *cluster = &hc.clusters_per_cell (top.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + res = rcsiter2string (ly, top.cell_index (), db::recursive_cluster_shape_iterator (hc, l1, top.cell_index (), i->id ())); + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (res, "TOP:(0,0;0,1000;1000,1000;1000,0);TOP/C1:(0,10;0,510;2000,510;2000,10);TOP/C2:(0,30;0,2030;500,2030;500,30);TOP/C2/C1:(0,50;0,550;2000,550;2000,50)"); +} + +TEST(41_HierClustersRecursiveClusterIterator) +{ + db::hier_clusters hc; + + db::Layout ly; + unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0)); + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + top.shapes (l1).insert (make_box (ly, db::Box (0, 0, 1000, 1000))); + + db::Cell &c1 = ly.cell (ly.add_cell ("C1")); + c1.shapes (l1).insert (make_box (ly, db::Box (0, 0, 2000, 500))); + top.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 10)))); + + db::Cell &c2 = ly.cell (ly.add_cell ("C2")); + c2.shapes (l1).insert (make_box (ly, db::Box (0, 0, 500, 2000))); + c2.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 20)))); + top.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans (db::Vector (0, 30)))); + + db::Connectivity conn; + conn.connect (l1, l1); + + hc.build (ly, top, db::ShapeIterator::Polygons, conn); + + std::string res; + int n = 0; + db::connected_clusters *cluster = &hc.clusters_per_cell (top.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + res = rciter2string (ly, top.cell_index (), db::recursive_cluster_iterator (hc, top.cell_index (), i->id ())); + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (res, "TOP;TOP/C1;TOP/C2;TOP/C2/C1"); +} + +static void normalize_layer (db::Layout &layout, std::vector &strings, unsigned int &layer) +{ + unsigned int new_layer = layout.insert_layer (); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + const db::Shapes &s = c->shapes (layer); + for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::Texts | db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); !i.at_end (); ++i) { + if (! i->is_text ()) { + db::Polygon poly; + i->polygon (poly); + c->shapes (new_layer).insert (db::PolygonRef (poly, layout.shape_repository ())); + } else { + db::Polygon poly (i->bbox ()); + unsigned int attr_id = (unsigned int) strings.size () + 1; + strings.push_back (i->text_string ()); + c->shapes (new_layer).insert (db::PolygonRefWithProperties (db::PolygonRef (poly, layout.shape_repository ()), attr_id)); + } + } + } + + layer = new_layer; +} + +static void copy_cluster_shapes (const std::string *&attrs, db::Shapes &out, db::cell_index_type ci, const db::hier_clusters &hc, db::local_cluster::id_type cluster_id, const db::ICplxTrans &trans, const db::Connectivity &conn) { // use property #1 to code the cell name + // use property #2 to code the attrs string for the first shape db::PropertiesRepository &pr = out.layout ()->properties_repository (); + + db::properties_id_type cell_pid = 0, cell_and_attr_pid = 0; + db::property_names_id_type pn_id = pr.prop_name_id (tl::Variant (1)); db::PropertiesRepository::properties_set pm; pm.insert (std::make_pair (pn_id, tl::Variant (out.layout ()->cell_name (ci)))); - db::properties_id_type cell_pid = pr.properties_id (pm); + cell_pid = pr.properties_id (pm); + + if (attrs && ! attrs->empty ()) { + db::property_names_id_type pn2_id = pr.prop_name_id (tl::Variant (2)); + pm.insert (std::make_pair (pn2_id, tl::Variant (*attrs))); + cell_and_attr_pid = pr.properties_id (pm); + } const db::connected_clusters &clusters = hc.clusters_per_cell (ci); const db::local_cluster &lc = clusters.cluster_by_id (cluster_id); @@ -438,7 +716,9 @@ static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { db::Polygon poly = s->obj ().transformed (trans * db::ICplxTrans (s->trans ())); - out.insert (db::PolygonWithProperties (poly, cell_pid)); + out.insert (db::PolygonWithProperties (poly, cell_and_attr_pid > 0 ? cell_and_attr_pid : cell_pid)); + cell_and_attr_pid = 0; + attrs = 0; // used } } @@ -452,7 +732,7 @@ static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const db::ICplxTrans t = trans * i->inst ().complex_trans (); db::cell_index_type cci = i->inst ().inst_ptr.cell_index (); - copy_cluster_shapes (out, cci, hc, i->id (), t, conn); + copy_cluster_shapes (attrs, out, cci, hc, i->id (), t, conn); } } @@ -460,12 +740,17 @@ static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const static void run_hc_test (tl::TestBase *_this, const std::string &file, const std::string &au_file) { db::Layout ly; - unsigned int l1 = 0, l2 = 0, l3 = 0; + unsigned int l0 = 0, l1 = 0, l2 = 0, l3 = 0; { db::LayerProperties p; db::LayerMap lmap; + p.layer = 0; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l0 = ly.insert_layer ()); + ly.set_properties (l0, p); + p.layer = 1; p.datatype = 0; lmap.map (db::LDPair (p.layer, p.datatype), l1 = ly.insert_layer ()); @@ -493,9 +778,10 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std reader.read (ly, options); } - normalize_layer (ly, l1); - normalize_layer (ly, l2); - normalize_layer (ly, l3); + std::vector strings; + normalize_layer (ly, strings, l1); + normalize_layer (ly, strings, l2); + normalize_layer (ly, strings, l3); // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 db::Connectivity conn; @@ -519,12 +805,25 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std continue; } + // collect strings + std::string attrs; + for (db::recursive_cluster_iterator rc (hc, *td, *c); ! rc.at_end (); ++rc) { + const db::local_cluster &rcc = hc.clusters_per_cell (rc.cell_index ()).cluster_by_id (rc.cluster_id ()); + for (db::local_cluster::attr_iterator a = rcc.begin_attr (); a != rcc.end_attr (); ++a) { + if (! attrs.empty ()) { + attrs += "/"; + } + attrs += std::string (ly.cell_name (rc.cell_index ())) + ":" + strings[*a - 1]; + } + } + net_layers.push_back (std::make_pair (0, ly.insert_layer ())); unsigned int lout = net_layers.back ().second; db::Shapes &out = ly.cell (*td).shapes (lout); - copy_cluster_shapes (out, *td, hc, *c, db::ICplxTrans (), conn); + const std::string *attrs_str = &attrs; + copy_cluster_shapes (attrs_str, out, *td, hc, *c, db::ICplxTrans (), conn); db::Polygon::area_type area = 0; for (db::Shapes::shape_iterator s = out.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { @@ -586,9 +885,10 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str reader.read (ly, options); } - normalize_layer (ly, l1); - normalize_layer (ly, l2); - normalize_layer (ly, l3); + std::vector strings; + normalize_layer (ly, strings, l1); + normalize_layer (ly, strings, l2); + normalize_layer (ly, strings, l3); // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 db::Connectivity conn; @@ -611,68 +911,69 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/" + au_file); } -TEST(41_HierClusters) +TEST(101_HierClusters) { run_hc_test (_this, "hc_test_l1.gds", "hc_test_au1.gds"); run_hc_test_with_backannotation (_this, "hc_test_l1.gds", "hc_test_au1b.gds"); } -TEST(42_HierClusters) +TEST(102_HierClusters) { run_hc_test (_this, "hc_test_l2.gds", "hc_test_au2.gds"); run_hc_test_with_backannotation (_this, "hc_test_l2.gds", "hc_test_au2b.gds"); } -TEST(43_HierClusters) +TEST(103_HierClusters) { run_hc_test (_this, "hc_test_l3.gds", "hc_test_au3.gds"); run_hc_test_with_backannotation (_this, "hc_test_l3.gds", "hc_test_au3b.gds"); } -TEST(44_HierClusters) +TEST(104_HierClusters) { run_hc_test (_this, "hc_test_l4.gds", "hc_test_au4.gds"); run_hc_test_with_backannotation (_this, "hc_test_l4.gds", "hc_test_au4b.gds"); } -TEST(45_HierClusters) +TEST(105_HierClusters) { run_hc_test (_this, "hc_test_l5.gds", "hc_test_au5.gds"); run_hc_test_with_backannotation (_this, "hc_test_l5.gds", "hc_test_au5b.gds"); } -TEST(46_HierClusters) +TEST(106_HierClusters) { run_hc_test (_this, "hc_test_l6.gds", "hc_test_au6.gds"); run_hc_test_with_backannotation (_this, "hc_test_l6.gds", "hc_test_au6b.gds"); } -TEST(47_HierClusters) +TEST(107_HierClusters) { run_hc_test (_this, "hc_test_l7.gds", "hc_test_au7.gds"); run_hc_test_with_backannotation (_this, "hc_test_l7.gds", "hc_test_au7b.gds"); } -TEST(48_HierClusters) +TEST(108_HierClusters) { run_hc_test (_this, "hc_test_l8.gds", "hc_test_au8.gds"); run_hc_test_with_backannotation (_this, "hc_test_l8.gds", "hc_test_au8b.gds"); } -TEST(49_HierClusters) +TEST(109_HierClusters) { run_hc_test (_this, "hc_test_l9.gds", "hc_test_au9.gds"); run_hc_test_with_backannotation (_this, "hc_test_l9.gds", "hc_test_au9b.gds"); } -TEST(50_HierClusters) +TEST(110_HierClusters) { run_hc_test (_this, "hc_test_l10.gds", "hc_test_au10.gds"); run_hc_test_with_backannotation (_this, "hc_test_l10.gds", "hc_test_au10b.gds"); } -TEST(51_HierClusters) +TEST(111_HierClusters) { run_hc_test (_this, "hc_test_l11.gds", "hc_test_au11.gds"); run_hc_test_with_backannotation (_this, "hc_test_l4.gds", "hc_test_au4b.gds"); } + diff --git a/testdata/algo/hc_test_au1.gds b/testdata/algo/hc_test_au1.gds index 21209e2b382763511db4ff4ccaac12374951ca93..09622935b3c618d130e22234e422b875240b90e0 100644 GIT binary patch delta 147 zcmeyyeS}AefsKKQDS|t<8 diff --git a/testdata/algo/hc_test_au1b.gds b/testdata/algo/hc_test_au1b.gds index e1f2615cf0fe676636519693db3807ef6812de1b..65406edd577fef87399a9e6f66fda4587df07684 100644 GIT binary patch delta 128 zcmaFGwSrrTfsKKQDS|@Bkby;zfq{jcfkB29x1*!H5}@WY5@S9?3ed?X7`T9rbN2W11Da3Rj>#F! lGv(N{LEe|rVRJUL@-xy0X|@6zp$`(6oX94@Bkby;zfq{jcfkB29x1*!H5}@WY5@S9?3ed?X7`T9rbN2W11Da3Rj>#F! TX_NU_8YZ7(cG%p<@`(`u|LrY( delta 153 zcmdnT*TAd9z{bGD6u}_F$i)7Lfro*EfssL%K?s>WQAt%8i}2(cW-mE*1_l{coH{o~ fonV}tz;u9svdL;JX_I+a9VYj%G;H=^{lo|W^m-QY diff --git a/testdata/algo/hc_test_l1.gds b/testdata/algo/hc_test_l1.gds index 49f6575ad1f267d3c81dfa31dbe35beab150dec4..e428b70b056e3779f0d75f7f56c75833122b7091 100644 GIT binary patch delta 128 zcmaFGx`IuKfsKKQDS|4<*i!(B?GO$T80@*y$tPW~vV8Fm5!2JLJ_Y5FD!@wrV=4ZG$j6d*pqz$MA%=j`w2$H2fM2*m6R3^J@Z zt=<@Qg3$tM2@^4vuwP(cU~XYx16pD Date: Sun, 16 Dec 2018 23:50:34 +0000 Subject: [PATCH 087/335] Enabled build without qt, Ruby and Python --- src/buddies/src/buddy_app.pri | 7 ++++++- src/db/unit_tests/dbTilingProcessor.cc | 7 +++---- src/pyastub/pya.cc | 3 ++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/buddies/src/buddy_app.pri b/src/buddies/src/buddy_app.pri index d5bafd153..5168eb801 100644 --- a/src/buddies/src/buddy_app.pri +++ b/src/buddies/src/buddy_app.pri @@ -10,7 +10,7 @@ include($$PWD/../../app.pri) # On Mac OSX, these buddy tools have to be built as ordinary command line tools; # not as bundles (*.app) mac { - CONFIG -= app_bundle + CONFIG -= app_bundle } # Since the main function is entirely unspecific, we can put it into a common @@ -42,3 +42,8 @@ equals(HAVE_PYTHON, "1") { DEFINES += BD_TARGET=$$TARGET LIBS += $$RUBYLIBFILE + +if(mac|linux*) { + LIBS += -ldl +} + diff --git a/src/db/unit_tests/dbTilingProcessor.cc b/src/db/unit_tests/dbTilingProcessor.cc index 516e5f613..862461b58 100644 --- a/src/db/unit_tests/dbTilingProcessor.cc +++ b/src/db/unit_tests/dbTilingProcessor.cc @@ -22,6 +22,7 @@ #include "tlUnitTest.h" +#include "tlThreads.h" #include "dbTilingProcessor.h" #include "dbTextWriter.h" @@ -32,8 +33,6 @@ #include "dbShapeProcessor.h" #include -#include -#include unsigned int get_rand() { @@ -403,8 +402,8 @@ public: void add (double x) const { - static QMutex lock; - QMutexLocker locker (&lock); + static tl::Mutex lock; + tl::MutexLocker locker (&lock); *mp_sum += x; *mp_n += 1; } diff --git a/src/pyastub/pya.cc b/src/pyastub/pya.cc index d63d44d71..fad0755f0 100644 --- a/src/pyastub/pya.cc +++ b/src/pyastub/pya.cc @@ -22,13 +22,14 @@ #include "pya.h" +#include "tlInternational.h" namespace pya { static void fail (const char *file, int line) { - throw tl::ScriptError (tl::to_string (QObject::tr ("Python support not compiled in")).c_str (), file, line, "missing_feature", std::vector ()); + throw tl::ScriptError (tl::to_string (tr ("Python support not compiled in")).c_str (), file, line, "missing_feature", std::vector ()); } static PythonInterpreter *sp_pya_interpreter = 0; From 7ceb4ed076fd993be85305066b5475ed10a0367f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 17 Dec 2018 20:31:24 +0100 Subject: [PATCH 088/335] Fixed an issue with the deep region: the layout pointer is not a good key for the backannotation cache. So we better use the unique ID - a concept introduced herein. --- src/db/db/dbDeepShapeStore.cc | 2 +- src/db/db/dbDeepShapeStore.h | 4 +- src/db/db/dbLayout.h | 4 +- src/tl/tl/tl.pro | 6 ++- src/tl/tl/tlUniqueId.cc | 43 +++++++++++++++ src/tl/tl/tlUniqueId.h | 81 ++++++++++++++++++++++++++++ src/tl/unit_tests/tlUniqueIdTests.cc | 51 ++++++++++++++++++ src/tl/unit_tests/unit_tests.pro | 1 + 8 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 src/tl/tl/tlUniqueId.cc create mode 100644 src/tl/tl/tlUniqueId.h create mode 100644 src/tl/unit_tests/tlUniqueIdTests.cc diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 2ff81f9e0..76ea5dfcb 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -320,7 +320,7 @@ DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db // derive a cell mapping for source to target. We employ a - DeliveryMappingCacheKey key (deep_layer.layout_index (), into_layout, into_cell); + DeliveryMappingCacheKey key (deep_layer.layout_index (), tl::id_of (into_layout), into_cell); std::map::iterator cm = m_delivery_mapping_cache.find (key); if (cm == m_delivery_mapping_cache.end ()) { diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 31b18f472..10553c268 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -301,7 +301,7 @@ private: { // NOTE: we shouldn't keep pointers here as the layouts may get deleted and recreated with the same address. // But as we don't access these objects that's fairly safe. - DeliveryMappingCacheKey (unsigned int _from_index, db::Layout *_into_layout, db::cell_index_type _into_cell) + DeliveryMappingCacheKey (unsigned int _from_index, tl::id_type _into_layout, db::cell_index_type _into_cell) : from_index (_from_index), into_layout (_into_layout), into_cell (_into_cell) { // .. nothing yet .. @@ -319,7 +319,7 @@ private: } unsigned int from_index; - db::Layout *into_layout; + tl::id_type into_layout; db::cell_index_type into_cell; }; diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index eb48b7727..76ecb0a7c 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -40,6 +40,7 @@ #include "tlString.h" #include "tlThreads.h" #include "tlObject.h" +#include "tlUniqueId.h" #include "gsi.h" #include @@ -460,7 +461,8 @@ class DB_PUBLIC Layout : public db::Object, public db::LayoutStateModel, public gsi::ObjectBase, - public tl::Object + public tl::Object, + public tl::UniqueId { public: typedef db::Box box_type; diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index d5c002aea..3805807d8 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -41,7 +41,8 @@ SOURCES = \ tlThreads.cc \ tlDeferredExecution.cc \ tlUri.cc \ - tlLongInt.cc + tlLongInt.cc \ + tlUniqueId.cc HEADERS = \ tlAlgorithm.h \ @@ -92,7 +93,8 @@ HEADERS = \ tlThreads.h \ tlDeferredExecution.h \ tlUri.h \ - tlLongInt.h + tlLongInt.h \ + tlUniqueId.h equals(HAVE_CURL, "1") { diff --git a/src/tl/tl/tlUniqueId.cc b/src/tl/tl/tlUniqueId.cc new file mode 100644 index 000000000..888b5b2b6 --- /dev/null +++ b/src/tl/tl/tlUniqueId.cc @@ -0,0 +1,43 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "tlUniqueId.h" +#include "tlThreads.h" + +namespace tl +{ + +static tl::Mutex s_lock; +static id_type s_next_id = 0; + +UniqueId::UniqueId () +{ + tl::MutexLocker locker (&s_lock); + do { + m_id = ++s_next_id; + } while (m_id == 0); +} + +} // namespace tl + + diff --git a/src/tl/tl/tlUniqueId.h b/src/tl/tl/tlUniqueId.h new file mode 100644 index 000000000..61a73c003 --- /dev/null +++ b/src/tl/tl/tlUniqueId.h @@ -0,0 +1,81 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_tlUniqueId +#define HDR_tlUniqueId + +#include "tlCommon.h" +#include + +namespace tl +{ + +typedef uint64_t id_type; + +/** + * @brief An object delivering a unique ID per object + * + * This class is supposed to provide a solution for the non-uniqueness + * issue of addresses. Instead of using addresses as keys, use the unique + * ID. The advantage is to be reproducible and guaranteed to be different + * for each allocation of an object (apart from overflow wrapping at 64bit - + * this this is pretty unlikely event). + * + * To supply an ID, derive your class from tl::UniqueId. Use + * "tl::id_of (object *)" the get the ID from that object. + * A null-pointer will give an ID of 0 which is reserved for + * "nothing". + * + * The type of the ID is tl::id_type. + */ +class TL_PUBLIC UniqueId +{ +public: + /** + * @brief @brief Creates a new object with a new ID + */ + UniqueId (); + + UniqueId (const UniqueId &) { } + UniqueId &operator= (const UniqueId &) { return *this; } + +private: + friend id_type id_of (const UniqueId *); + + id_type m_id; +}; + +/** + * @brief Gets the ID of the object pointed to by o + * + * Returns 0 in a null pointer and only then. + */ +inline id_type id_of (const UniqueId *o) +{ + return o ? o->m_id : 0; +} + +} // namespace tl + +#endif + diff --git a/src/tl/unit_tests/tlUniqueIdTests.cc b/src/tl/unit_tests/tlUniqueIdTests.cc new file mode 100644 index 000000000..20d734939 --- /dev/null +++ b/src/tl/unit_tests/tlUniqueIdTests.cc @@ -0,0 +1,51 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "tlUniqueId.h" +#include "tlUnitTest.h" + +#include + +namespace { + class MyClass : public tl::UniqueId { + public: + MyClass () { } + }; +} + +// basic parsing ability +TEST(1) +{ + tl::id_type id, id0; + + id = tl::id_of (0); + EXPECT_EQ (id, tl::id_type (0)); + + std::auto_ptr ptr; + ptr.reset (new MyClass ()); + id0 = id = tl::id_of (ptr.get ()); + EXPECT_NE (id, tl::id_type (0)); + + ptr.reset (new MyClass ()); + id = tl::id_of (ptr.get ()); + EXPECT_EQ (id, id0 + 1); +} diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index 89d63ec6c..f10fc4e70 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -35,6 +35,7 @@ SOURCES = \ tlHttpStream.cc \ tlInt128Support.cc \ tlLongInt.cc \ + tlUniqueIdTests.cc !equals(HAVE_QT, "0") { From 2c4e84fdf29aed6ff5917ce43d8fe78e6b689b8a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Dec 2018 23:56:01 +0100 Subject: [PATCH 089/335] WIP: netlist property framework - NetlistProperty is the base class for objects that can be attached to shapes for annotation - First property type implemented: net name is a way to annotate net names --- src/db/db/db.pro | 7 +- src/db/db/dbNetlistProperty.cc | 196 ++++++++++++++++ src/db/db/dbNetlistProperty.h | 235 ++++++++++++++++++++ src/db/db/gsiDeclDbNetlistProperty.cc | 116 ++++++++++ src/db/unit_tests/dbNetlistPropertyTests.cc | 69 ++++++ src/db/unit_tests/unit_tests.pro | 3 +- src/rba/unit_tests/rba.cc | 1 + testdata/ruby/dbNetlistProperty.rb | 81 +++++++ 8 files changed, 705 insertions(+), 3 deletions(-) create mode 100644 src/db/db/dbNetlistProperty.cc create mode 100644 src/db/db/dbNetlistProperty.h create mode 100644 src/db/db/gsiDeclDbNetlistProperty.cc create mode 100644 src/db/unit_tests/dbNetlistPropertyTests.cc create mode 100644 testdata/ruby/dbNetlistProperty.rb diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 3ac6db9e4..63b3c2fdc 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -141,7 +141,9 @@ SOURCES = \ dbLocalOperation.cc \ dbHierProcessor.cc \ dbDeepRegion.cc \ - dbHierNetworkProcessor.cc + dbHierNetworkProcessor.cc \ + dbNetlistProperty.cc \ + gsiDeclDbNetlistProperty.cc HEADERS = \ dbArray.h \ @@ -250,7 +252,8 @@ HEADERS = \ dbDeepShapeStore.h \ dbHierarchyBuilder.h \ dbLocalOperation.h \ - dbHierProcessor.h + dbHierProcessor.h \ + dbNetlistProperty.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbNetlistProperty.cc b/src/db/db/dbNetlistProperty.cc new file mode 100644 index 000000000..12e3213fa --- /dev/null +++ b/src/db/db/dbNetlistProperty.cc @@ -0,0 +1,196 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlistProperty.h" +#include "tlString.h" + +#include + +namespace tl +{ + +void VariantUserClass::destroy (void *p) const +{ + delete (db::NetlistProperty *) p; +} + +bool VariantUserClass::equal (const void *a, const void *b) const +{ + const db::NetlistProperty *pa = (db::NetlistProperty *) a; + const db::NetlistProperty *pb = (db::NetlistProperty *) b; + if (typeid (*pa) == typeid (*pb)) { + return pa->equals (pb); + } else { + return false; + } +} + +bool VariantUserClass::less (const void *a, const void *b) const +{ + const db::NetlistProperty *pa = (db::NetlistProperty *) a; + const db::NetlistProperty *pb = (db::NetlistProperty *) b; + if (typeid (*pa) == typeid (*pb)) { + return pa->less (pb); + } else { + return typeid (*pa).before (typeid (*pb)); + } +} + +void *VariantUserClass::clone (const void *p) const +{ + return ((const db::NetlistProperty *) p)->clone (); +} + +std::string VariantUserClass::to_string (const void *p) const +{ + return ((const db::NetlistProperty *) p)->to_string (); +} + +void VariantUserClass::read (void *p, tl::Extractor &ex) const +{ + ((db::NetlistProperty *) p)->read (ex); +} + +void VariantUserClass::assign (void *self, const void *other) const +{ + db::NetlistProperty *pself = (db::NetlistProperty *) self; + const db::NetlistProperty *pother = (const db::NetlistProperty *) other; + tl_assert (typeid (*pself) == typeid (*pother)); + pself->assign (pother); +} + +void *VariantUserClass::deref_proxy (tl::Object *proxy) const +{ + return proxy; +} + +void VariantUserClass::register_instance (const tl::VariantUserClassBase *inst, bool is_const) +{ + VariantUserClassBase::register_instance (inst, typeid (db::NetlistProperty), is_const); +} + +void VariantUserClass::unregister_instance (const tl::VariantUserClassBase *inst, bool is_const) +{ + VariantUserClassBase::unregister_instance (inst, typeid (db::NetlistProperty), is_const); +} + +} + +namespace db +{ + +// -------------------------------------------------------------------------------------------- +// NetlistProperty Implementation + +NetlistProperty::NetlistProperty () +{ + // .. nothing yet .. +} + +NetlistProperty::NetlistProperty (const NetlistProperty &) +{ + // .. nothing yet .. +} + +NetlistProperty::~NetlistProperty () +{ + // .. nothing yet .. +} + +const tl::VariantUserClass *NetlistProperty::variant_class () +{ + static tl::VariantUserClass s_cls; + return &s_cls; +} + +// -------------------------------------------------------------------------------------------- +// NetlistName Implementation + +NetNameProperty::NetNameProperty () + : NetlistProperty () +{ + // .. nothing yet .. +} + +NetNameProperty::NetNameProperty (const NetNameProperty &other) + : NetlistProperty (other), m_name (other.m_name) +{ + // .. nothing yet .. +} + +NetNameProperty::NetNameProperty (const std::string &n) + : NetlistProperty (), m_name (n) +{ + // .. nothing yet .. +} + +NetNameProperty &NetNameProperty::operator= (const NetNameProperty &other) +{ + NetlistProperty::operator= (other); + if (this != &other) { + m_name = other.m_name; + } + return *this; +} + +void NetNameProperty::set_name (const std::string &n) +{ + m_name = n; +} + +bool NetNameProperty::equals (const NetlistProperty *p) const +{ + const NetNameProperty *pp = static_cast (p); + return NetlistProperty::equals (p) && m_name == pp->m_name; +} + +bool NetNameProperty::less (const NetlistProperty *p) const +{ + if (! NetlistProperty::equals (p)) { + return NetlistProperty::less (p); + } else { + const NetNameProperty *pp = static_cast (p); + return m_name < pp->m_name; + } +} + +void NetNameProperty::assign (const NetlistProperty *p) +{ + NetlistProperty::assign (p); + const NetNameProperty *pp = static_cast (p); + m_name = pp->m_name; +} + + +static const char *valid_netname_chars = "_.$[]():-,"; + +std::string NetNameProperty::to_string () const +{ + return "name:" + tl::to_word_or_quoted_string (m_name, valid_netname_chars); +} + +void NetNameProperty::read (tl::Extractor &ex) +{ + ex.read_word_or_quoted (m_name, valid_netname_chars); +} + +} diff --git a/src/db/db/dbNetlistProperty.h b/src/db/db/dbNetlistProperty.h new file mode 100644 index 000000000..efb41136a --- /dev/null +++ b/src/db/db/dbNetlistProperty.h @@ -0,0 +1,235 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbCommon.h" +#include "tlVariant.h" + +#include + +namespace db +{ + class NetlistProperty; +} + +namespace tl +{ + class Extractor; + + // specialization of tl::VariantUserClass for the purpose of NetlistProperty representation + template <> class DB_PUBLIC tl::VariantUserClass + : public tl::VariantUserClassBase + { + public: + // creation not supported + virtual void *create () const { tl_assert (false); } + + virtual void destroy (void *p) const; + virtual bool equal (const void *a, const void *b) const; + virtual bool less (const void *a, const void *b) const; + virtual void *clone (const void *p) const; + virtual const char *name () const { return ""; } + virtual bool is_const () const { return false; } + virtual const gsi::ClassBase *gsi_cls () const { return 0; } + virtual const tl::EvalClass *eval_cls () const { return 0; } + virtual std::string to_string (const void *p) const; + virtual void read (void *p, tl::Extractor &ex) const; + virtual void assign (void *self, const void *other) const; + virtual void *deref_proxy (tl::Object *proxy) const; + + db::NetlistProperty *get (void *ptr) const { return reinterpret_cast (ptr); } + const db::NetlistProperty *get (const void *ptr) const { return reinterpret_cast (ptr); } + + protected: + void register_instance (const tl::VariantUserClassBase *inst, bool is_const); + void unregister_instance (const tl::VariantUserClassBase *inst, bool is_const); + }; + +} + +namespace db +{ + +/** + * @brief The base class for a netlist property attached to a shape + * + * This class provides a wrapper for binding a netlist property + * to a tl::Variant. Hence it can be kept as a shape property + * in the context of db::Layout's propery repo. + */ +class DB_PUBLIC NetlistProperty +{ +public: + /** + * @brief Gets the class descriptor for keeping the object inside a tl::Variant + * + * For a Variant that owns a NetlistProperty object, use + * + * @code + * db::NetlistProperty *prop = new db::NetlistProperty (); + * bool shared = true; // the variant will own the object + * tl::Variant prop_in_var (prop, prop->variant_class (), shared); + * @endcode + */ + static const tl::VariantUserClass *variant_class (); + + /** + * @brief Constructor + */ + NetlistProperty (); + + /** + * @brief Copy constructor + */ + NetlistProperty (const NetlistProperty &other); + + /** + * @brief (virtual) Destructor + */ + virtual ~NetlistProperty (); + + /** + * @brief Clones the object + */ + virtual NetlistProperty *clone () const + { + return new NetlistProperty (*this); + } + + /** + * @brief Compares two objects (equal). Both types are guaranteed to be the same. + */ + virtual bool equals (const NetlistProperty *) const + { + return true; + } + + /** + * @brief Compares two objects (less). Both types are guaranteed to be the same. + */ + virtual bool less (const NetlistProperty *) const + { + return false; + } + + /** + * @brief Assigned the other object to self. Both types are guaranteed to be identical. + */ + virtual void assign (const NetlistProperty *) + { + // .. nothing yet .. + } + + /** + * @brief Converts to a string + */ + virtual std::string to_string () const + { + return std::string (); + } + + /** + * @brief Pulls the object from a string + */ + virtual void read (tl::Extractor &) + { + // .. nothing yet .. + } +}; + +/** + * @brief A property meaning a net name + */ +class DB_PUBLIC NetNameProperty + : public db::NetlistProperty +{ +public: + /** + * @brief Creates a netlist name property without a specific name + */ + NetNameProperty (); + + /** + * @brief copy constructor + */ + NetNameProperty (const NetNameProperty &other); + + /** + * @brief Creates a netlist name property with the given name + */ + NetNameProperty (const std::string &n); + + /** + * @brief Assignment + */ + NetNameProperty &operator= (const NetNameProperty &other); + + /** + * @brief Sets the name + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Clones the object + */ + virtual NetlistProperty *clone () const + { + return new NetNameProperty (*this); + } + + /** + * @brief Compares two objects (equal). Both types are guaranteed to be the same. + */ + virtual bool equals (const NetlistProperty *) const; + + /** + * @brief Compares two objects (less). Both types are guaranteed to be the same. + */ + virtual bool less (const NetlistProperty *) const; + + /** + * @brief Assigned the other object to self. Both types are guaranteed to be identical. + */ + virtual void assign (const NetlistProperty *); + + /** + * @brief Converts to a string + */ + virtual std::string to_string () const; + + /** + * @brief Pulls the object from a string + */ + virtual void read (tl::Extractor &); + +private: + std::string m_name; +}; + +} diff --git a/src/db/db/gsiDeclDbNetlistProperty.cc b/src/db/db/gsiDeclDbNetlistProperty.cc new file mode 100644 index 000000000..30b4de22f --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistProperty.cc @@ -0,0 +1,116 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "gsiDecl.h" +#include "dbNetlistProperty.h" + +namespace gsi +{ + +// --------------------------------------------------------------- +// db::NetlistProperty binding + +static db::NetlistProperty *new_property () +{ + return new db::NetlistProperty (); +} + +static db::NetlistProperty *from_string (const std::string &str) +{ + tl::Extractor ex (str.c_str ()); + if (ex.at_end ()) { + + return new db::NetlistProperty (); + + } else if (ex.test ("name")) { + + ex.test (":"); + std::auto_ptr n (new db::NetNameProperty ()); + n->read (ex); + return n.release (); + + } else { + + return 0; + + } +} + +gsi::Class decl_NetlistProperty ("db", "NetlistProperty", + gsi::constructor ("new", &new_property, + "@brief Creates a plain netlist property" + ) + + gsi::constructor ("from_s", &from_string, gsi::arg ("str"), + "@brief Creates a netlist property from a string\n" + "This method can turn the string returned by \\to_string back into a property object.\n" + "@param str The string to read the property from\n" + "@return A fresh property object created from the string\n" + ) + + gsi::method ("to_s", &db::NetlistProperty::to_string, + "@brief Convert the property to a string.\n" + "@return The string representing this property\n" + ), + "@brief A generic base class for netlist properties.\n" + "\n" + "Netlist properties are used to annotate shapes or other objects with net properties. " + "Netlist properties are net names or device ports. " + "Netlist properties can be stored inside property sets. " + "This class provides the base class for such netlist properties." + "\n\n" + "This class was introduced in version 0.26.\n" +); + +// --------------------------------------------------------------- +// db::NetNameProperty binding + +static db::NetNameProperty *new_netname () +{ + return new db::NetNameProperty (); +} + +static db::NetNameProperty *new_netname2 (const std::string &n) +{ + return new db::NetNameProperty (n); +} + +gsi::Class decl_NetNameProperty (decl_NetlistProperty, "db", "NetNameProperty", + gsi::constructor ("new", &new_netname, + "@brief Creates a new net name property object without a specific name" + ) + + gsi::constructor ("new", &new_netname2, gsi::arg ("name"), + "@brief Creates a new net name property object with the given name" + ) + + gsi::method ("name=", &db::NetNameProperty::set_name, gsi::arg ("n"), + "@brief Sets the name\n" + ) + + gsi::method ("name", &db::NetNameProperty::name, + "@brief Gets the name\n" + ), + "@brief A net name property.\n" + "\n" + "The netlist property annotates a shape or other object with a net name." + "\n\n" + "This class was introduced in version 0.26.\n" +); + +} diff --git a/src/db/unit_tests/dbNetlistPropertyTests.cc b/src/db/unit_tests/dbNetlistPropertyTests.cc new file mode 100644 index 000000000..7f82e8072 --- /dev/null +++ b/src/db/unit_tests/dbNetlistPropertyTests.cc @@ -0,0 +1,69 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlistProperty.h" + +#include "tlUnitTest.h" +#include "tlVariant.h" + +#include + +TEST(1_Basic) +{ + db::NetNameProperty name; + EXPECT_EQ (name.to_string (), "name:''"); + + name.set_name ("abc"); + EXPECT_EQ (name.to_string (), "name:abc"); + EXPECT_EQ (name.name (), "abc"); + + db::NetNameProperty n2 = name; + EXPECT_EQ (n2.name (), "abc"); + + n2 = db::NetNameProperty ("xyz"); + EXPECT_EQ (n2.name (), "xyz"); + + n2.set_name ("\"quoted\""); + EXPECT_EQ (n2.to_string (), "name:'\"quoted\"'"); + + tl::Extractor ex ("net42"); + n2.read (ex); + EXPECT_EQ (n2.name (), "net42"); +} + +TEST(2_Variants) +{ + std::auto_ptr nn (new db::NetNameProperty ()); + nn->set_name ("net42"); + + tl::Variant v (nn.release (), db::NetlistProperty::variant_class (), true); + EXPECT_EQ (v.is_user (), true); + EXPECT_EQ (dynamic_cast(v.to_user ()).name (), "net42"); + EXPECT_EQ (v.to_string (), "name:net42"); + + tl::Variant vv = v; + v = tl::Variant (); + EXPECT_EQ (v.is_user (), false); + EXPECT_EQ (vv.is_user (), true); + EXPECT_EQ (dynamic_cast(vv.to_user ()).name (), "net42"); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 052a65b78..f67e2f202 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -58,7 +58,8 @@ SOURCES = \ dbHierProcessorTests.cc \ dbDeepRegionTests.cc \ dbDeepShapeStoreTests.cc \ - dbHierNetworkProcessorTests.cc + dbHierNetworkProcessorTests.cc \ + dbNetlistPropertyTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index d5acbf36c..e6d7dcac7 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -109,6 +109,7 @@ RUBYTEST (dbLayoutTest, "dbLayoutTest.rb") RUBYTEST (dbLayoutDiff, "dbLayoutDiff.rb") RUBYTEST (dbLayoutQuery, "dbLayoutQuery.rb") RUBYTEST (dbMatrix, "dbMatrix.rb") +RUBYTEST (dbNetlistProperty, "dbNetlistProperty.rb") RUBYTEST (dbPathTest, "dbPathTest.rb") RUBYTEST (dbPCells, "dbPCells.rb") RUBYTEST (dbPointTest, "dbPointTest.rb") diff --git a/testdata/ruby/dbNetlistProperty.rb b/testdata/ruby/dbNetlistProperty.rb new file mode 100644 index 000000000..c7dae4292 --- /dev/null +++ b/testdata/ruby/dbNetlistProperty.rb @@ -0,0 +1,81 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2018 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBNetlistProperty_TestClass < TestBase + + def test_1_Basic + + np = RBA::NetlistProperty::new + assert_equal(np.is_a?(RBA::NetlistProperty), true) + assert_equal(np.to_s, "") + + np2 = RBA::NetlistProperty::from_s("") + assert_equal(np2.is_a?(RBA::NetlistProperty), true) + + end + + def test_2_NetName + + np = RBA::NetNameProperty::new + assert_equal(np.is_a?(RBA::NetlistProperty), true) + assert_equal(np.is_a?(RBA::NetNameProperty), true) + assert_equal(np.to_s, "name:''") + + np.name = "abc" + assert_equal(np.to_s, "name:abc") + assert_equal(np.name, "abc") + + np2 = RBA::NetlistProperty::from_s("name:xyz") + assert_equal(np2.is_a?(RBA::NetlistProperty), true) + assert_equal(np2.is_a?(RBA::NetNameProperty), true) + assert_equal(np2.name, "xyz") + + end + + def test_3_VariantBinding + + ly = RBA::Layout::new + l1 = ly.insert_layer(RBA::LayerInfo::new(1, 0)) + tc = ly.create_cell("TOP") + shapes = tc.shapes(l1) + + box = shapes.insert(RBA::Box::new(0, 0, 1000, 2000)) + + # dry run + box.set_property("a", 17) + assert_equal(box.property("a"), 17) + + # set a NetNameProperty: + box.set_property("nn", RBA::NetNameProperty::new("net42")) + nn = box.property("nn") + assert_equal(nn.is_a?(RBA::NetlistProperty), true) + assert_equal(nn.is_a?(RBA::NetNameProperty), true) + assert_equal(nn.name, "net42") + + end + +end + +load("test_epilogue.rb") From d78a25efe4a8e0547523f2eb20fb755df5aca2e9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 19 Dec 2018 23:41:39 +0100 Subject: [PATCH 090/335] WIP: new classes for netlist representation. --- src/db/db/db.pro | 6 +- src/db/db/dbNetlist.cc | 383 ++++++++++++ src/db/db/dbNetlist.h | 1022 +++++++++++++++++++++++++++++++++ src/db/db/dbNetlistProperty.h | 51 ++ 4 files changed, 1460 insertions(+), 2 deletions(-) create mode 100644 src/db/db/dbNetlist.cc create mode 100644 src/db/db/dbNetlist.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 63b3c2fdc..8b10ea896 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -143,7 +143,8 @@ SOURCES = \ dbDeepRegion.cc \ dbHierNetworkProcessor.cc \ dbNetlistProperty.cc \ - gsiDeclDbNetlistProperty.cc + gsiDeclDbNetlistProperty.cc \ + dbNetlist.cc HEADERS = \ dbArray.h \ @@ -253,7 +254,8 @@ HEADERS = \ dbHierarchyBuilder.h \ dbLocalOperation.h \ dbHierProcessor.h \ - dbNetlistProperty.h + dbNetlistProperty.h \ + dbNetlist.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc new file mode 100644 index 000000000..61b954c79 --- /dev/null +++ b/src/db/db/dbNetlist.cc @@ -0,0 +1,383 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlist.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// Pin class implementation + +Pin::Pin () +{ + // .. nothing yet .. +} + +Pin::Pin (Circuit *circuit, const std::string &name) + : m_circuit (circuit), m_name (name) +{ + // .. nothing yet .. +} + +// -------------------------------------------------------------------------------- +// Port class implementation + +Port::Port () +{ + // .. nothing yet .. +} + +Port::Port (Device *device, port_id_type port_id) + : m_device (device), m_port_id (port_id) +{ + // .. nothing yet .. +} + +const DevicePortDefinition * +Port::port_def () const +{ + const DeviceClass *dc = device_class (); + if (dc && m_port_id < dc->port_definitions ().size ()) { + return &dc->port_definitions ()[m_port_id]; + } else { + return 0; + } +} + +const DeviceClass * +Port::device_class () const +{ + const Device *device = m_device.get (); + return device ? device->device_class () : 0; +} + +// -------------------------------------------------------------------------------- +// Device class implementation + +Device::Device (DeviceClass *device_class) + : m_device_class (device_class) +{ + // .. nothing yet .. +} + +// -------------------------------------------------------------------------------- +// SubCircuit class implementation + +SubCircuit::SubCircuit () +{ + // .. nothing yet .. +} + +SubCircuit::SubCircuit (Circuit *circuit) + : m_circuit (circuit) +{ + // .. nothing yet .. +} + +// -------------------------------------------------------------------------------- +// NetPortRef class implementation + +NetPortRef::NetPortRef () +{ + // .. nothing yet .. +} + +NetPortRef::NetPortRef (Port *port) + : m_port (port) +{ + // .. nothing yet .. +} + +// -------------------------------------------------------------------------------- +// NetPinRef class implementation + +NetPinRef::NetPinRef () +{ + // .. nothing yet .. +} + +NetPinRef::NetPinRef (Pin *pin) + : m_pin (pin) +{ + // .. nothing yet .. +} + +NetPinRef::NetPinRef (Pin *pin, SubCircuit *circuit) + : m_pin (pin), m_subcircuit (circuit) +{ + // .. nothing yet .. +} + +// -------------------------------------------------------------------------------- +// Net class implementation + +Net::Net () +{ + // .. nothing yet .. +} + +Net::Net (const Net &other) +{ + operator= (other); +} + +Net &Net::operator= (const Net &other) +{ + if (this != &other) { + m_name = other.m_name; + m_pins = other.m_pins; + m_ports = other.m_ports; + } + return *this; +} + +void Net::clear () +{ + m_name.clear (); + m_ports.clear (); + m_pins.clear (); +} + +void Net::set_name (const std::string &name) +{ + m_name = name; +} + +void Net::add_pin (const NetPinRef &pin) +{ + m_pins.push_back (pin); +} + +void Net::add_port (const NetPortRef &port) +{ + m_ports.push_back (port); +} + +// -------------------------------------------------------------------------------- +// Circuit class implementation + +Circuit::Circuit () +{ + // .. nothing yet .. +} + +Circuit::Circuit (const Circuit &other) +{ + operator= (other); +} + +Circuit &Circuit::operator= (const Circuit &other) +{ + if (this != &other) { + m_name = other.m_name; + for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { + add_pin (new Pin (*i)); + } + for (const_device_iterator i = other.begin_devices (); i != other.end_devices (); ++i) { + add_device (new Device (*i)); + } + for (const_net_iterator i = other.begin_nets (); i != other.end_nets (); ++i) { + add_net (new Net (*i)); + } + for (const_sub_circuit_iterator i = other.begin_sub_circuits (); i != other.end_sub_circuits (); ++i) { + add_sub_circuit (new SubCircuit (*i)); + } + } + return *this; +} + +void Circuit::clear () +{ + m_name.clear (); + m_pins.clear (); + m_devices.clear (); + m_nets.clear (); + m_sub_circuits.clear (); +} + +void Circuit::set_name (const std::string &name) +{ + m_name = name; +} + +void Circuit::set_cell_index (const db::cell_index_type ci) +{ + m_cell_index = ci; +} + +void Circuit::add_pin (Pin *pin) +{ + m_pins.push_back (pin); +} + +void Circuit::remove_pin (Pin *pin) +{ + m_pins.erase (pin); +} + +void Circuit::add_net (Net *net) +{ + m_nets.push_back (net); +} + +void Circuit::remove_net (Net *net) +{ + m_nets.erase (net); +} + +void Circuit::add_device (Device *device) +{ + m_devices.push_back (device); +} + +void Circuit::remove_device (Device *device) +{ + m_devices.erase (device); +} + +void Circuit::add_sub_circuit (SubCircuit *sub_circuit) +{ + m_sub_circuits.push_back (sub_circuit); +} + +void Circuit::remove_sub_circuit (SubCircuit *sub_circuit) +{ + m_sub_circuits.erase (sub_circuit); +} + +// -------------------------------------------------------------------------------- +// DeviceClass class implementation + +DeviceClass::DeviceClass () +{ + // .. nothing yet .. +} + +DeviceClass::DeviceClass (const DeviceClass & /*other*/) +{ + // .. nothing yet .. +} + +DeviceClass &DeviceClass::operator= (const DeviceClass & /*other*/) +{ + // .. nothing yet .. + return *this; +} + +const std::string &DeviceClass::name () const +{ + static std::string no_name; + return no_name; +} + +const std::string &DeviceClass::description () const +{ + static std::string no_description; + return no_description; +} + +const std::vector &DeviceClass::port_definitions () const +{ + static std::vector no_defs; + return no_defs; +} + +// -------------------------------------------------------------------------------- +// GenericDeviceClass class implementation + +GenericDeviceClass::GenericDeviceClass () +{ + // .. nothing yet .. +} + +GenericDeviceClass::GenericDeviceClass (const GenericDeviceClass &other) +{ + operator= (other); +} + +GenericDeviceClass &GenericDeviceClass::operator= (const GenericDeviceClass &other) +{ + if (this != &other) { + m_port_definitions = other.m_port_definitions; + m_name = other.m_name; + m_description = other.m_description; + } + return *this; +} + +// -------------------------------------------------------------------------------- +// Netlist class implementation + +Netlist::Netlist () +{ + // .. nothing yet .. +} + +Netlist::Netlist (const Netlist &other) +{ + operator= (other); +} + +Netlist &Netlist::operator= (const Netlist &other) +{ + if (this != &other) { + for (const_circuit_iterator i = other.begin_circuits (); i != other.end_circuits (); ++i) { + add_circuit (new Circuit (*i)); + } + + m_device_classes.clear (); + for (const_device_class_iterator dc = other.begin_device_classes (); dc != other.end_device_classes (); ++dc) { + m_device_classes.push_back (dc->clone ()); + } + } + + return *this; +} + +void Netlist::clear () +{ + m_device_classes.clear (); + m_circuits.clear (); +} + +void Netlist::add_circuit (Circuit *circuit) +{ + m_circuits.push_back (circuit); +} + +void Netlist::remove_circuit (Circuit *circuit) +{ + m_circuits.erase (circuit); +} + +void Netlist::add_device_class (DeviceClass *device_class) +{ + m_device_classes.push_back (device_class); +} + +void Netlist::remove_device_class (DeviceClass *device_class) +{ + m_device_classes.erase (device_class); +} + +} diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h new file mode 100644 index 000000000..958a91263 --- /dev/null +++ b/src/db/db/dbNetlist.h @@ -0,0 +1,1022 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbNetlist +#define _HDR_dbNetlist + +#include "dbCommon.h" +#include "dbTypes.h" +#include "tlObjectCollection.h" + +#include + +namespace db +{ + +class Circuit; +class Device; +class DeviceClass; +class DevicePortDefinition; + +/** + * @brief The definition of a pin of a circuit + * + * A pin is some place other nets can connect to a circuit. + */ +class DB_PUBLIC Pin + : public tl::Object +{ +public: + /** + * @brief Default constructor + */ + Pin (); + + /** + * @brief Creates a port of the given circuit with the given name. + */ + Pin (Circuit *circuit, const std::string &name); + + /** + * @brief Gets the circuit reference + */ + const Circuit *circuit () const + { + return m_circuit.get (); + } + + /** + * @brief Gets the name of the pin + */ + const std::string &name () const + { + return m_name; + } + +private: + tl::weak_ptr m_circuit; + std::string m_name; +}; + +/** + * @brief The definition of a port of a device + * + * A port is a connection a device can make. This is the port + * of an actual device. It corresponds to a device class by + * the port ID which is essentially the index of the port + * in the device classes' port list. + */ +class DB_PUBLIC Port + : public tl::Object +{ +public: + typedef size_t port_id_type; + + /** + * @brief Default constructor + */ + Port (); + + /** + * @brief Creates a port of the given device and port ID + */ + Port (Device *device, port_id_type port_id); + + /** + * @brief Gets the device reference + */ + const Device *device () const + { + return m_device.get (); + } + + /** + * @brief Gets the port id + */ + port_id_type port_id () const + { + return m_port_id; + } + + /** + * @brief Gets the port definition + * + * Returns 0 if the port is not a valid port reference. + */ + const DevicePortDefinition *port_def () const; + + /** + * @brief Returns the device class + */ + const DeviceClass *device_class () const; + +private: + tl::weak_ptr m_device; + port_id_type m_port_id; +}; + +/** + * @brief An actual device + * + * This class represents the incarnation of a specific device. + * The device has a class which specifies a type. This class + * is intended for subclassing. + * A specific device subclass is supposed to correspond to + * a specific device class. + */ +class DB_PUBLIC Device + : public tl::Object +{ +public: + /** + * @brief The constructor + */ + Device (DeviceClass *device_class); + + /** + * @brief Gets the device class + */ + const DeviceClass *device_class () const + { + return m_device_class.get (); + } + +private: + tl::weak_ptr m_device_class; +}; + +/** + * @brief A subcircuit of a circuit + * + * This class essentially is a reference to another circuit + */ +class DB_PUBLIC SubCircuit + : public tl::Object +{ +public: + /** + * @brief Default constructor + */ + SubCircuit (); + + /** + * @brief Creates a subcircuit reference to the given circuit + */ + SubCircuit (Circuit *circuit); + + /** + * @brief Gets the circuit the reference points to (const version) + */ + const Circuit *circuit () const + { + return m_circuit.get (); + } + + /** + * @brief Gets the circuit the reference points to (non-const version) + */ + Circuit *circuit () + { + return m_circuit.get (); + } + + +private: + tl::weak_ptr m_circuit; +}; + +/** + * @brief A reference to a port of a device + * + * A port must always refer to a device inside the current circuit. + */ +class DB_PUBLIC NetPortRef +{ + /** + * @brief Default constructor + */ + NetPortRef (); + + /** + * @brief Creates a pin reference to the given pin of the current circuit + */ + NetPortRef (Port *port); + + /** + * @brief Gets the port reference + */ + Port *port () + { + return m_port.get (); + } + + /** + * @brief Gets the port reference (const version) + */ + const Port *port () const + { + return m_port.get (); + } + +private: + tl::weak_ptr m_port; +}; + +/** + * @brief A reference to a pin inside a net + * + * A pin belongs to a subcircuit. + * If the subcircuit reference is 0, the pin is a pin of the current circuit + * (upward pin). + */ +class DB_PUBLIC NetPinRef +{ + /** + * @brief Default constructor + */ + NetPinRef (); + + /** + * @brief Creates a pin reference to the given pin of the current circuit + */ + NetPinRef (Pin *pin); + + /** + * @brief Creates a pin reference to the given pin of the given subcircuit + */ + NetPinRef (Pin *pin, SubCircuit *circuit); + + /** + * @brief Gets the pin reference + */ + Pin *pin () + { + return m_pin.get (); + } + + /** + * @brief Gets the pin reference (const version) + */ + const Pin *pin () const + { + return m_pin.get (); + } + + /** + * @brief Gets the subcircuit reference + */ + SubCircuit *subcircuit () + { + return m_subcircuit.get (); + } + + /** + * @brief Gets the subcircuit reference (const version) + */ + const SubCircuit *subcircuit () const + { + return m_subcircuit.get (); + } + +private: + tl::weak_ptr m_pin; + tl::weak_ptr m_subcircuit; +}; + +/** + * @brief A net + * + * A net connects ports of devices and pins or circuits + */ +class DB_PUBLIC Net + : public tl::Object +{ +public: + typedef std::list port_list; + typedef port_list::const_iterator const_port_iterator; + typedef std::list pin_list; + typedef pin_list::const_iterator const_pin_iterator; + + /** + * @brief Constructor + * + * Creates an empty circuit. + */ + Net (); + + /** + * @brief Copy constructor + */ + Net (const Net &other); + + /** + * @brief Assignment + */ + Net &operator= (const Net &other); + + /** + * @brief Clears the circuit + */ + void clear (); + + /** + * @brief Sets the name of the circuit + */ + void set_name (const std::string &name); + + /** + * @brief Gets the name of the circuit + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Adds a pin to this net + */ + void add_pin (const NetPinRef &pin); + + /** + * @brief Begin iterator for the pins of the net (const version) + */ + const_pin_iterator begin_pins () const + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (const version) + */ + const_pin_iterator end_pins () const + { + return m_pins.end (); + } + + /** + * @brief Adds a port to this net + */ + void add_port (const NetPortRef &port); + + /** + * @brief Begin iterator for the ports of the net (const version) + */ + const_port_iterator begin_ports () const + { + return m_ports.begin (); + } + + /** + * @brief End iterator for the ports of the net (const version) + */ + const_port_iterator end_ports () const + { + return m_ports.end (); + } + +private: + port_list m_ports; + pin_list m_pins; + std::string m_name; +}; + +/** + * @brief A circuit + * + * A circuit is a list of nets, of subcircuit references and actual + * devices. + */ +class DB_PUBLIC Circuit + : public tl::Object +{ +public: + typedef tl::shared_collection pin_list; + typedef pin_list::const_iterator const_pin_iterator; + typedef pin_list::iterator pin_iterator; + typedef tl::shared_collection device_list; + typedef device_list::const_iterator const_device_iterator; + typedef device_list::iterator device_iterator; + typedef tl::shared_collection net_list; + typedef net_list::const_iterator const_net_iterator; + typedef net_list::iterator net_iterator; + typedef tl::shared_collection sub_circuit_list; + typedef sub_circuit_list::const_iterator const_sub_circuit_iterator; + typedef sub_circuit_list::iterator sub_circuit_iterator; + + /** + * @brief Constructor + * + * Creates an empty circuit. + */ + Circuit (); + + /** + * @brief Copy constructor + */ + Circuit (const Circuit &other); + + /** + * @brief Assignment + */ + Circuit &operator= (const Circuit &other); + + /** + * @brief Clears the circuit + */ + void clear (); + + /** + * @brief Sets the name of the circuit + */ + void set_name (const std::string &name); + + /** + * @brief Gets the name of the circuit + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the layout cell reference for this circuit + * + * The layout cell reference links a circuit to a layout cell. + */ + void set_cell_index (const db::cell_index_type ci); + + /** + * @brief Gets the layout cell index + */ + db::cell_index_type cell_index () const + { + return m_cell_index; + } + + /** + * @brief Adds a pin to this circuit + * + * The circuit takes over ownership of the object. + */ + void add_pin (Pin *pin); + + /** + * @brief Deletes a pin from the circuit + */ + void remove_pin (Pin *pin); + + /** + * @brief Begin iterator for the pins of the circuit (non-const version) + */ + pin_iterator begin_pins () + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the circuit (non-const version) + */ + pin_iterator end_pins () + { + return m_pins.end (); + } + + /** + * @brief Begin iterator for the pins of the circuit (const version) + */ + const_pin_iterator begin_pins () const + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the circuit (const version) + */ + const_pin_iterator end_pins () const + { + return m_pins.end (); + } + + /** + * @brief Adds a net to this circuit + * + * The circuit takes over ownership of the object. + */ + void add_net (Net *net); + + /** + * @brief Deletes a net from the circuit + */ + void remove_net (Net *net); + + /** + * @brief Begin iterator for the nets of the circuit (non-const version) + */ + net_iterator begin_nets () + { + return m_nets.begin (); + } + + /** + * @brief End iterator for the nets of the circuit (non-const version) + */ + net_iterator end_nets () + { + return m_nets.end (); + } + + /** + * @brief Begin iterator for the nets of the circuit (const version) + */ + const_net_iterator begin_nets () const + { + return m_nets.begin (); + } + + /** + * @brief End iterator for the nets of the circuit (const version) + */ + const_net_iterator end_nets () const + { + return m_nets.end (); + } + + /** + * @brief Adds a device to this circuit + * + * The circuit takes over ownership of the object. + */ + void add_device (Device *device); + + /** + * @brief Deletes a device from the circuit + */ + void remove_device (Device *device); + + /** + * @brief Begin iterator for the devices of the circuit (non-const version) + */ + device_iterator begin_devices () + { + return m_devices.begin (); + } + + /** + * @brief End iterator for the devices of the circuit (non-const version) + */ + device_iterator end_devices () + { + return m_devices.end (); + } + + /** + * @brief Begin iterator for the devices of the circuit (const version) + */ + const_device_iterator begin_devices () const + { + return m_devices.begin (); + } + + /** + * @brief End iterator for the devices of the circuit (const version) + */ + const_device_iterator end_devices () const + { + return m_devices.end (); + } + + /** + * @brief Adds a subcircuit to this circuit + * + * The circuit takes over ownership of the object. + */ + void add_sub_circuit (SubCircuit *sub_circuit); + + /** + * @brief Deletes a subcircuit from the circuit + */ + void remove_sub_circuit (SubCircuit *sub_circuit); + + /** + * @brief Begin iterator for the subcircuits of the circuit (non-const version) + */ + sub_circuit_iterator begin_sub_circuits () + { + return m_sub_circuits.begin (); + } + + /** + * @brief End iterator for the subcircuits of the circuit (non-const version) + */ + sub_circuit_iterator end_sub_circuits () + { + return m_sub_circuits.end (); + } + + /** + * @brief Begin iterator for the subcircuits of the circuit (const version) + */ + const_sub_circuit_iterator begin_sub_circuits () const + { + return m_sub_circuits.begin (); + } + + /** + * @brief End iterator for the subcircuits of the circuit (const version) + */ + const_sub_circuit_iterator end_sub_circuits () const + { + return m_sub_circuits.end (); + } + +private: + std::string m_name; + db::cell_index_type m_cell_index; + net_list m_nets; + pin_list m_pins; + device_list m_devices; + sub_circuit_list m_sub_circuits; +}; + +/** + * @brief A device port definition + */ +class DB_PUBLIC DevicePortDefinition +{ +public: + /** + * @brief Creates an empty device port definition + */ + DevicePortDefinition () + : m_name (), m_description () + { + // .. nothing yet .. + } + + /** + * @brief Creates a device port definition with the given name and description + */ + DevicePortDefinition (const std::string &name, const std::string &description) + : m_name (name), m_description (description) + { + // .. nothing yet .. + } + + /** + * @brief Gets the port name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the port name + */ + void set_name (const std::string &n) + { + m_name = n; + } + + /** + * @brief Gets the port description + */ + const std::string &description () const + { + return m_description; + } + + /** + * @brief Sets the port description + */ + void set_description (const std::string &d) + { + m_description = d; + } + +private: + std::string m_name, m_description; +}; + +/** + * @brief A device class + * + * A device class describes a type of device. + */ +class DB_PUBLIC DeviceClass + : public tl::Object +{ +public: + /** + * @brief Constructor + * + * Creates an empty circuit. + */ + DeviceClass (); + + /** + * @brief Copy constructor + * NOTE: do not use this copy constructor as the device class + * is intended to subclassing. + */ + DeviceClass (const DeviceClass &other); + + /** + * @brief Assignment + * NOTE: do not use this copy constructor as the device class + * is intended to subclassing. + */ + DeviceClass &operator= (const DeviceClass &other); + + /** + * @brief Clears the circuit + */ + virtual DeviceClass *clone () const + { + return new DeviceClass (*this); + } + + /** + * @brief Gets the name of the device class + * + * The name is a formal name which identifies the class. + */ + virtual const std::string &name () const; + + /** + * @brief Gets the description text for the device class + * + * The description text is a human-readable text that + * identifies the device class. + */ + virtual const std::string &description () const; + + /** + * @brief Gets the port definitions + * + * The port definitions indicate what ports the device offers. + * The number of ports is constant per class. The index of the port + * is used as an ID of the port, hence the order must be static. + */ + virtual const std::vector &port_definitions () const; +}; + +/** + * @brief A generic device class + * + * The generic device class is a push version of the DeviceClassBase + */ +class DB_PUBLIC GenericDeviceClass + : public DeviceClass +{ +public: + /** + * @brief Constructor + * + * Creates an empty circuit. + */ + GenericDeviceClass (); + + /** + * @brief Copy constructor + */ + GenericDeviceClass (const GenericDeviceClass &other); + + /** + * @brief Assignment + */ + GenericDeviceClass &operator= (const GenericDeviceClass &other); + + /** + * @brief Clears the circuit + */ + virtual DeviceClass *clone () const + { + return new GenericDeviceClass (*this); + } + + /** + * @brief Gets the name of the device class + * + * The name is a formal name which identifies the class. + */ + virtual const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the device name + */ + void set_name (const std::string &n) + { + m_name = n; + } + + /** + * @brief Gets the description text for the device class + * + * The description text is a human-readable text that + * identifies the device class. + */ + virtual const std::string &description () const + { + return m_description; + } + + /** + * @brief Sets the description text + */ + void set_description (const std::string &d) + { + m_description = d; + } + + /** + * @brief Gets the port definitions + * + * The port definitions indicate what ports the device offers. + * The number of ports is constant per class. The index of the port + * is used as an ID of the port, hence the order must be static. + */ + virtual const std::vector &port_definitions () const + { + return m_port_definitions; + } + + /** + * @brief Adds a port definition + */ + void add_port_definition (const DevicePortDefinition &pd) + { + m_port_definitions.push_back (pd); + } + + /** + * @brief Clears the port definition + */ + void clear_port_definitions () + { + m_port_definitions.clear (); + } + +private: + std::vector m_port_definitions; + std::string m_name, m_description; +}; + +/** + * @brief The netlist class + * + * This class represents a hierarchical netlist. + * The main components of a netlist are circuits and device classes. + * The circuits represent cells, the device classes type of devices. + */ +class DB_PUBLIC Netlist + : public tl::Object +{ +public: + typedef tl::shared_collection circuit_list; + typedef circuit_list::const_iterator const_circuit_iterator; + typedef circuit_list::iterator circuit_iterator; + typedef tl::shared_collection device_class_list; + typedef device_class_list::const_iterator const_device_class_iterator; + typedef device_class_list::iterator device_class_iterator; + + /** + * @brief Constructor + * + * This constructor creates an empty hierarchical netlist + */ + Netlist (); + + /** + * @brief Copy constructor + */ + Netlist (const Netlist &other); + + /** + * @brief Assignment + */ + Netlist &operator= (const Netlist &other); + + /** + * @brief Clears the netlist + */ + void clear (); + + /** + * @brief Adds a circuit to this netlist + * + * The netlist takes over ownership of the object. + */ + void add_circuit (Circuit *circuit); + + /** + * @brief Deletes a circuit from the netlist + */ + void remove_circuit (Circuit *circuit); + + /** + * @brief Begin iterator for the circuits of the netlist (non-const version) + */ + circuit_iterator begin_circuits () + { + return m_circuits.begin (); + } + + /** + * @brief End iterator for the circuits of the netlist (non-const version) + */ + circuit_iterator end_circuits () + { + return m_circuits.end (); + } + + /** + * @brief Begin iterator for the circuits of the netlist (const version) + */ + const_circuit_iterator begin_circuits () const + { + return m_circuits.begin (); + } + + /** + * @brief End iterator for the circuits of the netlist (const version) + */ + const_circuit_iterator end_circuits () const + { + return m_circuits.end (); + } + + /** + * @brief Adds a device class to this netlist + * + * The netlist takes over ownership of the object. + */ + void add_device_class (DeviceClass *device_class); + + /** + * @brief Deletes a device class from the netlist + */ + void remove_device_class (DeviceClass *device_class); + + /** + * @brief Begin iterator for the device classes of the netlist (non-const version) + */ + device_class_iterator begin_device_classes () + { + return m_device_classes.begin (); + } + + /** + * @brief End iterator for the device classes of the netlist (non-const version) + */ + device_class_iterator end_device_classes () + { + return m_device_classes.end (); + } + + /** + * @brief Begin iterator for the device classes of the netlist (const version) + */ + const_device_class_iterator begin_device_classes () const + { + return m_device_classes.begin (); + } + + /** + * @brief End iterator for the device classes of the netlist (const version) + */ + const_device_class_iterator end_device_classes () const + { + return m_device_classes.end (); + } + +private: + circuit_list m_circuits; + device_class_list m_device_classes; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistProperty.h b/src/db/db/dbNetlistProperty.h index efb41136a..76c47bd40 100644 --- a/src/db/db/dbNetlistProperty.h +++ b/src/db/db/dbNetlistProperty.h @@ -20,7 +20,11 @@ */ +#ifndef _HDR_dbNetlistProperty +#define _HDR_dbNetlistProperty + #include "dbCommon.h" +#include "dbNetlist.h" #include "tlVariant.h" #include @@ -232,4 +236,51 @@ private: std::string m_name; }; +#if 0 // @@@ +/** + * @brief A reference to an actual port + */ +class DB_PUBLIC DevicePortRef + : public db::NetlistProperty +{ +public: + DevicePortRef (db::NetPortRef *port); + + // ... + +private: + tl::weak_ptr mp_port; +}; + +/** + * @brief An abstrace reference to a port + * + * Abstract references are created when turning a string back into a port. + * Abstract references can be turned into actual port references using + * "to_actual_ref". + */ +class DB_PUBLIC DevicePortAbstractRef + : public db::NetlistProperty +{ +public: + DevicePortAbstractRef (const std::string &device_name, const std::string &port_name); + + // ... + + /** + * @brief Turns an abstract reference into an actual one + * + * The returned object is either 0, if the translation cannot be done or + * and new'd NetPortRef object. It's the responsibility of the caller + * to delete this object when it's no longer used. + */ + NetPortRef *to_actual_ref (const db::Netlist *netlist) const; + +private: + std::string m_device_name, m_port_name; +}; +#endif + } + +#endif From 18346945df36b741e33f6674bb4f7d0bcfdf678c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 20 Dec 2018 22:11:20 +0100 Subject: [PATCH 091/335] WIP: some refactoring. --- src/db/db/dbNetlist.cc | 172 ++++++++++++++++++++++++------------- src/db/db/dbNetlist.h | 186 ++++++++++++++++++++++++----------------- 2 files changed, 224 insertions(+), 134 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 61b954c79..2f9d43a2c 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -29,48 +29,17 @@ namespace db // Pin class implementation Pin::Pin () + : m_id (0) { // .. nothing yet .. } Pin::Pin (Circuit *circuit, const std::string &name) - : m_circuit (circuit), m_name (name) + : m_circuit (circuit), m_name (name), m_id (0) { // .. nothing yet .. } -// -------------------------------------------------------------------------------- -// Port class implementation - -Port::Port () -{ - // .. nothing yet .. -} - -Port::Port (Device *device, port_id_type port_id) - : m_device (device), m_port_id (port_id) -{ - // .. nothing yet .. -} - -const DevicePortDefinition * -Port::port_def () const -{ - const DeviceClass *dc = device_class (); - if (dc && m_port_id < dc->port_definitions ().size ()) { - return &dc->port_definitions ()[m_port_id]; - } else { - return 0; - } -} - -const DeviceClass * -Port::device_class () const -{ - const Device *device = m_device.get (); - return device ? device->device_class () : 0; -} - // -------------------------------------------------------------------------------- // Device class implementation @@ -102,12 +71,30 @@ NetPortRef::NetPortRef () // .. nothing yet .. } -NetPortRef::NetPortRef (Port *port) - : m_port (port) +NetPortRef::NetPortRef (Device *device, size_t port_id) + : m_device (device), m_port_id (port_id) { // .. nothing yet .. } +const DevicePortDefinition * +NetPortRef::port_def () const +{ + const DeviceClass *dc = device_class (); + if (dc && m_port_id < dc->port_definitions ().size ()) { + return &dc->port_definitions ()[m_port_id]; + } else { + return 0; + } +} + +const DeviceClass * +NetPortRef::device_class () const +{ + const Device *device = m_device.get (); + return device ? device->device_class () : 0; +} + // -------------------------------------------------------------------------------- // NetPinRef class implementation @@ -116,18 +103,23 @@ NetPinRef::NetPinRef () // .. nothing yet .. } -NetPinRef::NetPinRef (Pin *pin) - : m_pin (pin) +NetPinRef::NetPinRef (size_t pin_id) + : m_pin_id (pin_id) { // .. nothing yet .. } -NetPinRef::NetPinRef (Pin *pin, SubCircuit *circuit) - : m_pin (pin), m_subcircuit (circuit) +NetPinRef::NetPinRef (size_t pin_id, SubCircuit *circuit) + : m_pin_id (pin_id), m_subcircuit (circuit) { // .. nothing yet .. } +const Pin *NetPinRef::pin () const +{ + return m_subcircuit->circuit ()->pin_by_id (m_pin_id); +} + // -------------------------------------------------------------------------------- // Net class implementation @@ -173,6 +165,24 @@ void Net::add_port (const NetPortRef &port) m_ports.push_back (port); } +void Net::translate_devices (const std::map &map) +{ + for (port_list::iterator i = m_ports.begin (); i != m_ports.end (); ++i) { + std::map::const_iterator m = map.find (i->device ()); + tl_assert (m != map.end ()); + i->set_device (m->second); + } +} + +void Net::translate_subcircuits (const std::map &map) +{ + for (pin_list::iterator i = m_pins.begin (); i != m_pins.end (); ++i) { + std::map::const_iterator m = map.find (i->subcircuit ()); + tl_assert (m != map.end ()); + i->set_subcircuit (m->second); + } +} + // -------------------------------------------------------------------------------- // Circuit class implementation @@ -189,23 +199,47 @@ Circuit::Circuit (const Circuit &other) Circuit &Circuit::operator= (const Circuit &other) { if (this != &other) { + m_name = other.m_name; + for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { - add_pin (new Pin (*i)); + add_pin (*i); } + + std::map device_table; for (const_device_iterator i = other.begin_devices (); i != other.end_devices (); ++i) { - add_device (new Device (*i)); - } - for (const_net_iterator i = other.begin_nets (); i != other.end_nets (); ++i) { - add_net (new Net (*i)); + Device *d = new Device (*i); + device_table [i.operator-> ()] = d; + add_device (d); } + + std::map sc_table; for (const_sub_circuit_iterator i = other.begin_sub_circuits (); i != other.end_sub_circuits (); ++i) { - add_sub_circuit (new SubCircuit (*i)); + SubCircuit *sc = new SubCircuit (*i); + sc_table [i.operator-> ()] = sc; + add_sub_circuit (sc); } + + for (const_net_iterator i = other.begin_nets (); i != other.end_nets (); ++i) { + Net *n = new Net (*i); + n->translate_devices (device_table); + n->translate_subcircuits (sc_table); + add_net (n); + } + } return *this; } +const Pin *Circuit::pin_by_id (size_t id) const +{ + if (id >= m_pins.size ()) { + return 0; + } else { + return &m_pins [id]; + } +} + void Circuit::clear () { m_name.clear (); @@ -225,14 +259,10 @@ void Circuit::set_cell_index (const db::cell_index_type ci) m_cell_index = ci; } -void Circuit::add_pin (Pin *pin) +void Circuit::add_pin (const Pin &pin) { m_pins.push_back (pin); -} - -void Circuit::remove_pin (Pin *pin) -{ - m_pins.erase (pin); + m_pins.back ().set_id (m_pins.size () - 1); } void Circuit::add_net (Net *net) @@ -265,6 +295,24 @@ void Circuit::remove_sub_circuit (SubCircuit *sub_circuit) m_sub_circuits.erase (sub_circuit); } +void Circuit::translate_circuits (const std::map &map) +{ + for (sub_circuit_iterator i = m_sub_circuits.begin (); i != m_sub_circuits.end (); ++i) { + std::map::const_iterator m = map.find (i->circuit ()); + tl_assert (m != map.end ()); + i->set_circuit (m->second); + } +} + +void Circuit::translate_device_classes (const std::map &map) +{ + for (device_iterator i = m_devices.begin (); i != m_devices.end (); ++i) { + std::map::const_iterator m = map.find (i->device_class ()); + tl_assert (m != map.end ()); + i->set_device_class (m->second); + } +} + // -------------------------------------------------------------------------------- // DeviceClass class implementation @@ -341,16 +389,28 @@ Netlist::Netlist (const Netlist &other) Netlist &Netlist::operator= (const Netlist &other) { if (this != &other) { - for (const_circuit_iterator i = other.begin_circuits (); i != other.end_circuits (); ++i) { - add_circuit (new Circuit (*i)); - } + std::map dct; m_device_classes.clear (); for (const_device_class_iterator dc = other.begin_device_classes (); dc != other.end_device_classes (); ++dc) { - m_device_classes.push_back (dc->clone ()); + DeviceClass *dc_new = dc->clone (); + dct [dc.operator-> ()] = dc_new; + m_device_classes.push_back (dc_new); } - } + std::map ct; + for (const_circuit_iterator i = other.begin_circuits (); i != other.end_circuits (); ++i) { + Circuit *ct_new = new Circuit (*i); + ct_new->translate_device_classes (dct); + ct [i.operator-> ()] = ct_new; + add_circuit (ct_new); + } + + for (circuit_iterator i = begin_circuits (); i != end_circuits (); ++i) { + i->translate_circuits (ct); + } + + } return *this; } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 958a91263..84e541049 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -26,8 +26,12 @@ #include "dbCommon.h" #include "dbTypes.h" #include "tlObjectCollection.h" +#include "tlVector.h" #include +#include +#include + namespace db { @@ -43,7 +47,6 @@ class DevicePortDefinition; * A pin is some place other nets can connect to a circuit. */ class DB_PUBLIC Pin - : public tl::Object { public: /** @@ -72,66 +75,25 @@ public: return m_name; } + /** + * @brief Gets the ID of the pin (only pins inside circuits have valid ID's) + */ + size_t id () const + { + return m_id; + } + private: + friend class Circuit; + tl::weak_ptr m_circuit; std::string m_name; -}; + size_t m_id; -/** - * @brief The definition of a port of a device - * - * A port is a connection a device can make. This is the port - * of an actual device. It corresponds to a device class by - * the port ID which is essentially the index of the port - * in the device classes' port list. - */ -class DB_PUBLIC Port - : public tl::Object -{ -public: - typedef size_t port_id_type; - - /** - * @brief Default constructor - */ - Port (); - - /** - * @brief Creates a port of the given device and port ID - */ - Port (Device *device, port_id_type port_id); - - /** - * @brief Gets the device reference - */ - const Device *device () const + void set_id (size_t id) { - return m_device.get (); + m_id = id; } - - /** - * @brief Gets the port id - */ - port_id_type port_id () const - { - return m_port_id; - } - - /** - * @brief Gets the port definition - * - * Returns 0 if the port is not a valid port reference. - */ - const DevicePortDefinition *port_def () const; - - /** - * @brief Returns the device class - */ - const DeviceClass *device_class () const; - -private: - tl::weak_ptr m_device; - port_id_type m_port_id; }; /** @@ -160,6 +122,14 @@ public: return m_device_class.get (); } + /** + * @brief Sets the device class + */ + void set_device_class (DeviceClass *cls) + { + m_device_class.reset (cls); + } + private: tl::weak_ptr m_device_class; }; @@ -199,6 +169,13 @@ public: return m_circuit.get (); } + /** + * @brief Sets the circuit reference + */ + void set_circuit (Circuit *c) + { + m_circuit.reset (c); + } private: tl::weak_ptr m_circuit; @@ -211,6 +188,7 @@ private: */ class DB_PUBLIC NetPortRef { +public: /** * @brief Default constructor */ @@ -219,26 +197,55 @@ class DB_PUBLIC NetPortRef /** * @brief Creates a pin reference to the given pin of the current circuit */ - NetPortRef (Port *port); + NetPortRef (Device *device, size_t port_id); /** - * @brief Gets the port reference + * @brief Gets the device reference */ - Port *port () + Device *device () { - return m_port.get (); + return m_device.get (); } /** - * @brief Gets the port reference (const version) + * @brief Gets the device reference (const version) */ - const Port *port () const + const Device *device () const { - return m_port.get (); + return m_device.get (); } + /** + * @brief Sets the device reference + */ + void set_device (Device *d) + { + m_device.reset (d); + } + + /** + * @brief Gets the port index + */ + size_t port_id () const + { + return m_port_id; + } + + /** + * @brief Gets the port definition + * + * Returns 0 if the port is not a valid port reference. + */ + const DevicePortDefinition *port_def () const; + + /** + * @brief Returns the device class + */ + const DeviceClass *device_class () const; + private: - tl::weak_ptr m_port; + tl::weak_ptr m_device; + size_t m_port_id; }; /** @@ -250,6 +257,7 @@ private: */ class DB_PUBLIC NetPinRef { +public: /** * @brief Default constructor */ @@ -258,29 +266,26 @@ class DB_PUBLIC NetPinRef /** * @brief Creates a pin reference to the given pin of the current circuit */ - NetPinRef (Pin *pin); + NetPinRef (size_t pin_id); /** * @brief Creates a pin reference to the given pin of the given subcircuit */ - NetPinRef (Pin *pin, SubCircuit *circuit); - - /** - * @brief Gets the pin reference - */ - Pin *pin () - { - return m_pin.get (); - } + NetPinRef (size_t pin_id, SubCircuit *circuit); /** * @brief Gets the pin reference (const version) */ - const Pin *pin () const + size_t pin_id () const { - return m_pin.get (); + return m_pin_id; } + /** + * @brief Gets the pin reference from the pin id + */ + const Pin *pin () const; + /** * @brief Gets the subcircuit reference */ @@ -297,8 +302,16 @@ class DB_PUBLIC NetPinRef return m_subcircuit.get (); } + /** + * @brief Sets the subcircuit reference + */ + void set_subcircuit (SubCircuit *s) + { + m_subcircuit.reset (s); + } + private: - tl::weak_ptr m_pin; + size_t m_pin_id; tl::weak_ptr m_subcircuit; }; @@ -394,9 +407,14 @@ public: } private: + friend class Circuit; + port_list m_ports; pin_list m_pins; std::string m_name; + + void translate_devices (const std::map &map); + void translate_subcircuits (const std::map &map); }; /** @@ -409,7 +427,7 @@ class DB_PUBLIC Circuit : public tl::Object { public: - typedef tl::shared_collection pin_list; + typedef tl::vector pin_list; typedef pin_list::const_iterator const_pin_iterator; typedef pin_list::iterator pin_iterator; typedef tl::shared_collection device_list; @@ -477,7 +495,7 @@ public: * * The circuit takes over ownership of the object. */ - void add_pin (Pin *pin); + void add_pin (const Pin &pin); /** * @brief Deletes a pin from the circuit @@ -500,6 +518,11 @@ public: return m_pins.end (); } + /** + * @brief Gets the pin by ID (the ID is basically the index) + */ + const Pin *pin_by_id (size_t id) const; + /** * @brief Begin iterator for the pins of the circuit (const version) */ @@ -649,12 +672,17 @@ public: } private: + friend class Netlist; + std::string m_name; db::cell_index_type m_cell_index; net_list m_nets; pin_list m_pins; device_list m_devices; sub_circuit_list m_sub_circuits; + + void translate_circuits (const std::map &map); + void translate_device_classes (const std::map &map); }; /** @@ -726,6 +754,8 @@ class DB_PUBLIC DeviceClass : public tl::Object { public: + typedef size_t port_id_type; + /** * @brief Constructor * From 4dd17c3cd48e161a4735a2e0271ab74c116ca71e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 20 Dec 2018 23:29:01 +0100 Subject: [PATCH 092/335] WIP: added tests for dbNetlist classes. --- src/db/db/dbNetlist.cc | 67 ++++-- src/db/db/dbNetlist.h | 105 ++++++---- src/db/unit_tests/dbNetlistTests.cc | 308 ++++++++++++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 3 +- 4 files changed, 419 insertions(+), 64 deletions(-) create mode 100644 src/db/unit_tests/dbNetlistTests.cc diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 2f9d43a2c..7cb00d18e 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -34,8 +34,8 @@ Pin::Pin () // .. nothing yet .. } -Pin::Pin (Circuit *circuit, const std::string &name) - : m_circuit (circuit), m_name (name), m_id (0) +Pin::Pin (const std::string &name) + : m_name (name), m_id (0) { // .. nothing yet .. } @@ -43,12 +43,17 @@ Pin::Pin (Circuit *circuit, const std::string &name) // -------------------------------------------------------------------------------- // Device class implementation -Device::Device (DeviceClass *device_class) - : m_device_class (device_class) +Device::Device (DeviceClass *device_class, const std::string &name) + : m_device_class (device_class), m_name (name) { // .. nothing yet .. } +void Device::set_name (const std::string &n) +{ + m_name = n; +} + // -------------------------------------------------------------------------------- // SubCircuit class implementation @@ -81,8 +86,8 @@ const DevicePortDefinition * NetPortRef::port_def () const { const DeviceClass *dc = device_class (); - if (dc && m_port_id < dc->port_definitions ().size ()) { - return &dc->port_definitions ()[m_port_id]; + if (dc) { + return dc->port_definition (m_port_id); } else { return 0; } @@ -115,9 +120,15 @@ NetPinRef::NetPinRef (size_t pin_id, SubCircuit *circuit) // .. nothing yet .. } -const Pin *NetPinRef::pin () const +const Pin *NetPinRef::pin (const db::Circuit *c) const { - return m_subcircuit->circuit ()->pin_by_id (m_pin_id); + if (! m_subcircuit.get ()) { + tl_assert (c != 0); + return c->pin_by_id (m_pin_id); + } else { + tl_assert (m_subcircuit->circuit () != 0); + return m_subcircuit->circuit ()->pin_by_id (m_pin_id); + } } // -------------------------------------------------------------------------------- @@ -177,9 +188,11 @@ void Net::translate_devices (const std::map &map) void Net::translate_subcircuits (const std::map &map) { for (pin_list::iterator i = m_pins.begin (); i != m_pins.end (); ++i) { - std::map::const_iterator m = map.find (i->subcircuit ()); - tl_assert (m != map.end ()); - i->set_subcircuit (m->second); + if (i->subcircuit ()) { + std::map::const_iterator m = map.find (i->subcircuit ()); + tl_assert (m != map.end ()); + i->set_subcircuit (m->second); + } } } @@ -321,14 +334,16 @@ DeviceClass::DeviceClass () // .. nothing yet .. } -DeviceClass::DeviceClass (const DeviceClass & /*other*/) +DeviceClass::DeviceClass (const DeviceClass &other) { - // .. nothing yet .. + operator= (other); } -DeviceClass &DeviceClass::operator= (const DeviceClass & /*other*/) +DeviceClass &DeviceClass::operator= (const DeviceClass &other) { - // .. nothing yet .. + if (this != &other) { + m_port_definitions = other.m_port_definitions; + } return *this; } @@ -344,10 +359,24 @@ const std::string &DeviceClass::description () const return no_description; } -const std::vector &DeviceClass::port_definitions () const +void DeviceClass::add_port_definition (const DevicePortDefinition &pd) { - static std::vector no_defs; - return no_defs; + m_port_definitions.push_back (pd); + m_port_definitions.back ().set_id (m_port_definitions.size () - 1); +} + +void DeviceClass::clear_port_definitions () +{ + m_port_definitions.clear (); +} + +const DevicePortDefinition *DeviceClass::port_definition (size_t id) const +{ + if (id < m_port_definitions.size ()) { + return & m_port_definitions [id]; + } else { + return 0; + } } // -------------------------------------------------------------------------------- @@ -366,7 +395,7 @@ GenericDeviceClass::GenericDeviceClass (const GenericDeviceClass &other) GenericDeviceClass &GenericDeviceClass::operator= (const GenericDeviceClass &other) { if (this != &other) { - m_port_definitions = other.m_port_definitions; + DeviceClass::operator= (other); m_name = other.m_name; m_description = other.m_description; } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 84e541049..4e4d90de9 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -55,17 +55,9 @@ public: Pin (); /** - * @brief Creates a port of the given circuit with the given name. + * @brief Creates a pin with the given name. */ - Pin (Circuit *circuit, const std::string &name); - - /** - * @brief Gets the circuit reference - */ - const Circuit *circuit () const - { - return m_circuit.get (); - } + Pin (const std::string &name); /** * @brief Gets the name of the pin @@ -112,7 +104,7 @@ public: /** * @brief The constructor */ - Device (DeviceClass *device_class); + Device (DeviceClass *device_class, const std::string &name = std::string ()); /** * @brief Gets the device class @@ -130,8 +122,22 @@ public: m_device_class.reset (cls); } + /** + * @brief Sets the name + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name + */ + const std::string &name () const + { + return m_name; + } + private: tl::weak_ptr m_device_class; + std::string m_name; }; /** @@ -283,8 +289,11 @@ public: /** * @brief Gets the pin reference from the pin id + * + * The circuit is the one where the net is defined. It is used to + * resolve outgoing pints. */ - const Pin *pin () const; + const Pin *pin (const Circuit *c) const; /** * @brief Gets the subcircuit reference @@ -695,7 +704,7 @@ public: * @brief Creates an empty device port definition */ DevicePortDefinition () - : m_name (), m_description () + : m_name (), m_description (), m_id (0) { // .. nothing yet .. } @@ -704,7 +713,7 @@ public: * @brief Creates a device port definition with the given name and description */ DevicePortDefinition (const std::string &name, const std::string &description) - : m_name (name), m_description (description) + : m_name (name), m_description (description), m_id (0) { // .. nothing yet .. } @@ -741,8 +750,24 @@ public: m_description = d; } + /** + * @brief Gets the port ID + */ + size_t id () const + { + return m_id; + } + private: + friend class DeviceClass; + std::string m_name, m_description; + size_t m_id; + + void set_id (size_t id) + { + m_id = id; + } }; /** @@ -807,7 +832,28 @@ public: * The number of ports is constant per class. The index of the port * is used as an ID of the port, hence the order must be static. */ - virtual const std::vector &port_definitions () const; + virtual const std::vector &port_definitions () const + { + return m_port_definitions; + } + + /** + * @brief Adds a port definition + */ + void add_port_definition (const DevicePortDefinition &pd); + + /** + * @brief Clears the port definition + */ + void clear_port_definitions (); + + /** + * @brief Gets the port definition from the ID + */ + const DevicePortDefinition *port_definition (size_t id) const; + +private: + std::vector m_port_definitions; }; /** @@ -881,36 +927,7 @@ public: m_description = d; } - /** - * @brief Gets the port definitions - * - * The port definitions indicate what ports the device offers. - * The number of ports is constant per class. The index of the port - * is used as an ID of the port, hence the order must be static. - */ - virtual const std::vector &port_definitions () const - { - return m_port_definitions; - } - - /** - * @brief Adds a port definition - */ - void add_port_definition (const DevicePortDefinition &pd) - { - m_port_definitions.push_back (pd); - } - - /** - * @brief Clears the port definition - */ - void clear_port_definitions () - { - m_port_definitions.clear (); - } - private: - std::vector m_port_definitions; std::string m_name, m_description; }; diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc new file mode 100644 index 000000000..c13d22e09 --- /dev/null +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -0,0 +1,308 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlist.h" + +#include "tlUnitTest.h" +#include "tlString.h" + +#include + +static std::string pd2string (const db::DevicePortDefinition &pd) +{ + return pd.name () + "(" + pd.description () + ") #" + tl::to_string (pd.id ()); +} + +TEST(1_DevicePortDefinition) +{ + db::DevicePortDefinition pd; + + EXPECT_EQ (pd2string (pd), "() #0"); + pd.set_name ("name"); + pd.set_description ("nothing yet"); + EXPECT_EQ (pd2string (pd), "name(nothing yet) #0"); + + db::DevicePortDefinition pd2; + pd2 = pd; + EXPECT_EQ (pd2string (pd2), "name(nothing yet) #0"); + pd2.set_name ("name2"); + pd2.set_description ("now it has something"); + EXPECT_EQ (pd2string (pd2), "name2(now it has something) #0"); + + db::DeviceClass dc; + dc.add_port_definition (pd); + dc.add_port_definition (pd2); + EXPECT_EQ (pd2string (dc.port_definitions ()[0]), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (dc.port_definitions ()[1]), "name2(now it has something) #1"); +} + +TEST(2_DeviceClass) +{ + db::DevicePortDefinition pd; + pd.set_name ("name"); + pd.set_description ("nothing yet"); + + db::DevicePortDefinition pd2; + pd2.set_name ("name2"); + pd2.set_description ("now it has something"); + + db::GenericDeviceClass dc; + dc.set_name ("devname"); + dc.set_description ("devdesc"); + EXPECT_EQ (dc.name (), "devname"); + EXPECT_EQ (dc.description (), "devdesc"); + dc.add_port_definition (pd); + dc.add_port_definition (pd2); + EXPECT_EQ (dc.port_definitions ().size (), size_t (2)); + EXPECT_EQ (pd2string (dc.port_definitions ()[0]), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (dc.port_definitions ()[1]), "name2(now it has something) #1"); + + EXPECT_EQ (pd2string (*dc.port_definition (dc.port_definitions ()[0].id ())), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (*dc.port_definition (dc.port_definitions ()[1].id ())), "name2(now it has something) #1"); + EXPECT_EQ (dc.port_definition (3), 0); + + db::GenericDeviceClass dc2 = dc; + EXPECT_EQ (dc2.name (), "devname"); + EXPECT_EQ (dc2.description (), "devdesc"); + EXPECT_EQ (dc2.port_definitions ().size (), size_t (2)); + EXPECT_EQ (pd2string (*dc2.port_definition (dc2.port_definitions ()[0].id ())), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (*dc2.port_definition (dc2.port_definitions ()[1].id ())), "name2(now it has something) #1"); + EXPECT_EQ (dc2.port_definition (3), 0); +} + +static std::string pins2string (const db::Circuit &c) +{ + std::string res; + for (db::Circuit::const_pin_iterator i = c.begin_pins (); i != c.end_pins (); ++i) { + if (!res.empty ()) { + res += ","; + } + res += i->name (); + res += "#" + tl::to_string (i->id ()); + } + return res; +} + +TEST(3_CircuitBasic) +{ + db::Circuit c; + c.set_name ("name"); + EXPECT_EQ (c.name (), "name"); + + db::Pin p1 ("p1"); + db::Pin p2 ("p2"); + c.add_pin (p1); + c.add_pin (p2); + EXPECT_EQ (pins2string (c), "p1#0,p2#1"); + + EXPECT_EQ (c.pin_by_id (0)->name (), "p1"); + EXPECT_EQ (c.pin_by_id (1)->name (), "p2"); + EXPECT_EQ (c.pin_by_id (2), 0); + + db::Circuit c2 = c; + EXPECT_EQ (c2.name (), "name"); + EXPECT_EQ (pins2string (c), "p1#0,p2#1"); + + EXPECT_EQ (c2.pin_by_id (0)->name (), "p1"); + EXPECT_EQ (c2.pin_by_id (1)->name (), "p2"); + EXPECT_EQ (c2.pin_by_id (2), 0); +} + +static std::string net2string (const db::Net &n, const db::Circuit *c = 0) +{ + std::string res; + for (db::Net::const_port_iterator i = n.begin_ports (); i != n.end_ports (); ++i) { + if (! res.empty ()) { + res += ","; + } + res += i->device () ? i->device ()->name () : "(null)"; + res += ":"; + res += i->port_def () ? i->port_def ()->name () : "(null)"; + } + for (db::Net::const_pin_iterator i = n.begin_pins (); i != n.end_pins (); ++i) { + if (! res.empty ()) { + res += ","; + } + if (i->subcircuit ()) { + res += i->subcircuit ()->circuit () ? i->subcircuit ()->circuit ()->name () : "(null)"; + res += ":"; + } else { + res += "+"; + } + res += i->pin (c) ? i->pin (c)->name () : "(null)"; + } + return res; +} + +static std::string nets2string (const db::Circuit &c) +{ + std::string res; + for (db::Circuit::const_net_iterator n = c.begin_nets (); n != c.end_nets (); ++n) { + res += net2string (*n, &c); + res += "\n"; + } + return res; +} + +TEST(4_CircuitDevices) +{ + db::GenericDeviceClass dc1; + dc1.set_name ("dc1"); + dc1.add_port_definition (db::DevicePortDefinition ("S", "Source")); + dc1.add_port_definition (db::DevicePortDefinition ("G", "Gate")); + dc1.add_port_definition (db::DevicePortDefinition ("D", "Drain")); + + db::GenericDeviceClass dc2; + dc2.set_name ("dc2"); + dc2.add_port_definition (db::DevicePortDefinition ("A", "")); + dc2.add_port_definition (db::DevicePortDefinition ("B", "")); + + std::auto_ptr c (new db::Circuit ()); + db::Device *d1 = new db::Device (&dc1, "d1"); + db::Device *d2a = new db::Device (&dc2, "d2a"); + db::Device *d2b = new db::Device (&dc2, "d2b"); + c->add_device (d1); + c->add_device (d2a); + c->add_device (d2b); + + db::Net *n1 = new db::Net (); + c->add_net (n1); + n1->add_port (db::NetPortRef (d1, 0)); + n1->add_port (db::NetPortRef (d2a, 0)); + + db::Net *n2 = new db::Net (); + c->add_net (n2); + n2->add_port (db::NetPortRef (d1, 1)); + n2->add_port (db::NetPortRef (d2a, 1)); + n2->add_port (db::NetPortRef (d2b, 0)); + + db::Net *n3 = new db::Net (); + c->add_net (n3); + n3->add_port (db::NetPortRef (d1, 2)); + n3->add_port (db::NetPortRef (d2b, 1)); + + EXPECT_EQ (nets2string (*c), + "d1:S,d2a:A\n" + "d1:G,d2a:B,d2b:A\n" + "d1:D,d2b:B\n" + ); + + db::Circuit cc = *c; + c.reset (0); + + EXPECT_EQ (nets2string (cc), + "d1:S,d2a:A\n" + "d1:G,d2a:B,d2b:A\n" + "d1:D,d2b:B\n" + ); +} + +static std::string netlist2string (const db::Netlist &nl) +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + res += "[" + c->name () + "]\n"; + res += nets2string (*c); + } + return res; +} + +TEST(4_NetlistSubcircuits) +{ + std::auto_ptr nl (new db::Netlist ()); + + db::GenericDeviceClass *dc = new db::GenericDeviceClass (); + dc->set_name ("dc2"); + dc->add_port_definition (db::DevicePortDefinition ("A", "")); + dc->add_port_definition (db::DevicePortDefinition ("B", "")); + nl->add_device_class (dc); + + db::Circuit *c1 = new db::Circuit (); + c1->set_name ("c1"); + c1->add_pin (db::Pin ("c1p1")); + c1->add_pin (db::Pin ("c1p2")); + nl->add_circuit (c1); + + db::Circuit *c2 = new db::Circuit (); + c2->set_name ("c2"); + c2->add_pin (db::Pin ("c2p1")); + c2->add_pin (db::Pin ("c2p2")); + nl->add_circuit (c2); + + db::Device *d = new db::Device (dc, "D"); + c2->add_device (d); + + db::SubCircuit *sc1 = new db::SubCircuit (c2); + c1->add_sub_circuit (sc1); + + db::SubCircuit *sc2 = new db::SubCircuit (c2); + c1->add_sub_circuit (sc2); + + db::Net *n2a = new db::Net (); + n2a->add_pin (db::NetPinRef (0)); + n2a->add_port (db::NetPortRef (d, 0)); + c2->add_net (n2a); + + db::Net *n2b = new db::Net (); + n2b->add_port (db::NetPortRef (d, 1)); + n2b->add_pin (db::NetPinRef (1)); + c2->add_net (n2b); + + db::Net *n1a = new db::Net (); + n1a->add_pin (db::NetPinRef (0)); + n1a->add_pin (db::NetPinRef (0, sc1)); + c1->add_net (n1a); + + db::Net *n1b = new db::Net (); + n1b->add_pin (db::NetPinRef (1, sc1)); + n1b->add_pin (db::NetPinRef (0, sc2)); + c1->add_net (n1b); + + db::Net *n1c = new db::Net (); + n1c->add_pin (db::NetPinRef (1, sc2)); + n1c->add_pin (db::NetPinRef (1)); + c1->add_net (n1c); + + EXPECT_EQ (netlist2string (*nl), + "[c1]\n" + "+c1p1,c2:c2p1\n" + "c2:c2p2,c2:c2p1\n" + "c2:c2p2,+c1p2\n" + "[c2]\n" + "D:A,+c2p1\n" + "D:B,+c2p2\n" + ); + + db::Netlist nl2 = *nl; + nl.reset (0); + + EXPECT_EQ (netlist2string (nl2), + "[c1]\n" + "+c1p1,c2:c2p1\n" + "c2:c2p2,c2:c2p1\n" + "c2:c2p2,+c1p2\n" + "[c2]\n" + "D:A,+c2p1\n" + "D:B,+c2p2\n" + ); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index f67e2f202..63a29945c 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -59,7 +59,8 @@ SOURCES = \ dbDeepRegionTests.cc \ dbDeepShapeStoreTests.cc \ dbHierNetworkProcessorTests.cc \ - dbNetlistPropertyTests.cc + dbNetlistPropertyTests.cc \ + dbNetlistTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC From 80999475f4383fac32a127034ec380f3fca6bd98 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Dec 2018 21:47:27 +0100 Subject: [PATCH 093/335] Added trans and name attributes to db::SubCircuit --- src/db/db/dbNetlist.cc | 10 ++++++++ src/db/db/dbNetlist.h | 36 +++++++++++++++++++++++++++++ src/db/unit_tests/dbNetlistTests.cc | 14 +++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 7cb00d18e..8305b9167 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -68,6 +68,16 @@ SubCircuit::SubCircuit (Circuit *circuit) // .. nothing yet .. } +void SubCircuit::set_name (const std::string &n) +{ + m_name = n; +} + +void SubCircuit::set_trans (const db::DCplxTrans &t) +{ + m_trans = t; +} + // -------------------------------------------------------------------------------- // NetPortRef class implementation diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 4e4d90de9..c238fe9ce 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -25,6 +25,7 @@ #include "dbCommon.h" #include "dbTypes.h" +#include "dbTrans.h" #include "tlObjectCollection.h" #include "tlVector.h" @@ -183,8 +184,43 @@ public: m_circuit.reset (c); } + /** + * @brief Sets the name of the subcircuit + * + * The name is one way to identify the subcircuit. The transformation is + * another one. + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name of the subcircuit + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the transformation describing the subcircuit + * + * The transformation is a natural description of a subcircuit + * (in contrast to the name) when deriving it from a layout. + */ + void set_trans (const db::DCplxTrans &trans); + + /** + * @brief Gets the transformation describing the subcircuit + */ + const db::DCplxTrans &trans () const + { + return m_trans; + } + + private: tl::weak_ptr m_circuit; + std::string m_name; + db::DCplxTrans m_trans; }; /** diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index c13d22e09..cc2653a15 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -306,3 +306,17 @@ TEST(4_NetlistSubcircuits) "D:B,+c2p2\n" ); } + +TEST(5_SubCircuit) +{ + db::SubCircuit sc, sc2; + + sc.set_name ("sc"); + EXPECT_EQ (sc.name (), "sc"); + sc.set_trans (db::DCplxTrans (2.5)); + EXPECT_EQ (sc.trans ().to_string (), "r0 *2.5 0,0"); + + sc2 = sc; + EXPECT_EQ (sc2.name (), "sc"); + EXPECT_EQ (sc2.trans ().to_string (), "r0 *2.5 0,0"); +} From 83a38037e59d52a711f2a41f76b6d63037d0aef5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Dec 2018 21:53:48 +0100 Subject: [PATCH 094/335] Added cluster_id property to db::Net --- src/db/db/dbNetlist.cc | 9 +++++++++ src/db/db/dbNetlist.h | 17 +++++++++++++++++ src/db/unit_tests/dbNetlistTests.cc | 18 ++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 8305b9167..1e48924b3 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -145,11 +145,13 @@ const Pin *NetPinRef::pin (const db::Circuit *c) const // Net class implementation Net::Net () + : m_cluster_id (0) { // .. nothing yet .. } Net::Net (const Net &other) + : m_cluster_id (0) { operator= (other); } @@ -160,6 +162,7 @@ Net &Net::operator= (const Net &other) m_name = other.m_name; m_pins = other.m_pins; m_ports = other.m_ports; + m_cluster_id = other.m_cluster_id; } return *this; } @@ -169,6 +172,7 @@ void Net::clear () m_name.clear (); m_ports.clear (); m_pins.clear (); + m_cluster_id = 0; } void Net::set_name (const std::string &name) @@ -176,6 +180,11 @@ void Net::set_name (const std::string &name) m_name = name; } +void Net::set_cluster_id (size_t ci) +{ + m_cluster_id = ci; +} + void Net::add_pin (const NetPinRef &pin) { m_pins.push_back (pin); diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index c238fe9ce..0e67f02d4 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -409,6 +409,22 @@ public: return m_name; } + /** + * @brief Sets the cluster ID of this net + * + * The cluster ID links the net to a cluster from the + * hierarchical layout netlist extractor. + */ + void set_cluster_id (size_t ci); + + /** + * @brief Gets the cluster ID + */ + size_t cluster_id () const + { + return m_cluster_id; + } + /** * @brief Adds a pin to this net */ @@ -457,6 +473,7 @@ private: port_list m_ports; pin_list m_pins; std::string m_name; + size_t m_cluster_id; void translate_devices (const std::map &map); void translate_subcircuits (const std::map &map); diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index cc2653a15..86f870f55 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -320,3 +320,21 @@ TEST(5_SubCircuit) EXPECT_EQ (sc2.name (), "sc"); EXPECT_EQ (sc2.trans ().to_string (), "r0 *2.5 0,0"); } + +TEST(6_Net) +{ + db::Net n, n2; + + n.set_name ("n"); + EXPECT_EQ (n.name (), "n"); + n.set_cluster_id (17); + EXPECT_EQ (int (n.cluster_id ()), 17); + + n2 = n; + EXPECT_EQ (n2.name (), "n"); + EXPECT_EQ (int (n2.cluster_id ()), 17); + + n.clear (); + EXPECT_EQ (n.name (), ""); + EXPECT_EQ (int (n.cluster_id ()), 0); +} From 654afa3ec29fea0f1f5781be190423d67da42f25 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Dec 2018 22:13:37 +0100 Subject: [PATCH 095/335] Upward references for db::Net and db::Circuit. --- src/db/db/dbNetlist.cc | 22 ++++++-- src/db/db/dbNetlist.h | 81 ++++++++++++++++++++++------- src/db/unit_tests/dbNetlistTests.cc | 7 +++ 3 files changed, 87 insertions(+), 23 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 1e48924b3..e18052b1a 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -62,8 +62,8 @@ SubCircuit::SubCircuit () // .. nothing yet .. } -SubCircuit::SubCircuit (Circuit *circuit) - : m_circuit (circuit) +SubCircuit::SubCircuit (Circuit *circuit, const std::string &name) + : m_circuit (circuit), m_name (name) { // .. nothing yet .. } @@ -145,13 +145,13 @@ const Pin *NetPinRef::pin (const db::Circuit *c) const // Net class implementation Net::Net () - : m_cluster_id (0) + : m_cluster_id (0), mp_circuit (0) { // .. nothing yet .. } Net::Net (const Net &other) - : m_cluster_id (0) + : m_cluster_id (0), mp_circuit (0) { operator= (other); } @@ -215,15 +215,22 @@ void Net::translate_subcircuits (const std::map= m_pins.size ()) { @@ -300,6 +312,7 @@ void Circuit::add_pin (const Pin &pin) void Circuit::add_net (Net *net) { m_nets.push_back (net); + net->set_circuit (this); } void Circuit::remove_net (Net *net) @@ -471,6 +484,7 @@ void Netlist::clear () void Netlist::add_circuit (Circuit *circuit) { m_circuits.push_back (circuit); + circuit->set_netlist (this); } void Netlist::remove_circuit (Circuit *circuit) diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 0e67f02d4..8935dca49 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -41,7 +41,7 @@ class Circuit; class Device; class DeviceClass; class DevicePortDefinition; - +class Netlist; /** * @brief The definition of a pin of a circuit * @@ -115,14 +115,6 @@ public: return m_device_class.get (); } - /** - * @brief Sets the device class - */ - void set_device_class (DeviceClass *cls) - { - m_device_class.reset (cls); - } - /** * @brief Sets the name */ @@ -137,8 +129,18 @@ public: } private: + friend class Circuit; + tl::weak_ptr m_device_class; std::string m_name; + + /** + * @brief Sets the device class + */ + void set_device_class (DeviceClass *dc) + { + m_device_class.reset (dc); + } }; /** @@ -158,7 +160,7 @@ public: /** * @brief Creates a subcircuit reference to the given circuit */ - SubCircuit (Circuit *circuit); + SubCircuit (Circuit *circuit, const std::string &name = std::string ()); /** * @brief Gets the circuit the reference points to (const version) @@ -176,14 +178,6 @@ public: return m_circuit.get (); } - /** - * @brief Sets the circuit reference - */ - void set_circuit (Circuit *c) - { - m_circuit.reset (c); - } - /** * @brief Sets the name of the subcircuit * @@ -216,11 +210,20 @@ public: return m_trans; } - private: + friend class Circuit; + tl::weak_ptr m_circuit; std::string m_name; db::DCplxTrans m_trans; + + /** + * @brief Sets the circuit reference + */ + void set_circuit (Circuit *c) + { + m_circuit.reset (c); + } }; /** @@ -391,6 +394,24 @@ public: */ Net &operator= (const Net &other); + /** + * @brief Gets the circuit the net lives in + * This pointer is 0 if the net is not part of a circuit. + */ + Circuit *circuit () + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the net lives in (const version) + * This pointer is 0 if the net is not part of a circuit. + */ + const Circuit *circuit () const + { + return mp_circuit; + } + /** * @brief Clears the circuit */ @@ -474,9 +495,11 @@ private: pin_list m_pins; std::string m_name; size_t m_cluster_id; + Circuit *mp_circuit; void translate_devices (const std::map &map); void translate_subcircuits (const std::map &map); + void set_circuit (Circuit *circuit); }; /** @@ -519,6 +542,24 @@ public: */ Circuit &operator= (const Circuit &other); + /** + * @brief Gets the netlist the circuit lives in + * This pointer is 0 if the circuit is not part of a netlist. + */ + Netlist *netlist () + { + return mp_netlist; + } + + /** + * @brief Gets the netlist the circuit lives in (const version) + * This pointer is 0 if the circuit is not part of a netlist. + */ + const Netlist *netlist () const + { + return mp_netlist; + } + /** * @brief Clears the circuit */ @@ -742,9 +783,11 @@ private: pin_list m_pins; device_list m_devices; sub_circuit_list m_sub_circuits; + Netlist *mp_netlist; void translate_circuits (const std::map &map); void translate_device_classes (const std::map &map); + void set_netlist (Netlist *netlist); }; /** diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 86f870f55..c2c37ba3a 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -186,9 +186,11 @@ TEST(4_CircuitDevices) c->add_device (d2b); db::Net *n1 = new db::Net (); + EXPECT_EQ (n1->circuit (), 0); c->add_net (n1); n1->add_port (db::NetPortRef (d1, 0)); n1->add_port (db::NetPortRef (d2a, 0)); + EXPECT_EQ (n1->circuit (), c.get ()); db::Net *n2 = new db::Net (); c->add_net (n2); @@ -209,6 +211,7 @@ TEST(4_CircuitDevices) db::Circuit cc = *c; c.reset (0); + EXPECT_EQ (cc.begin_nets ()->circuit (), &cc); EXPECT_EQ (nets2string (cc), "d1:S,d2a:A\n" @@ -238,10 +241,12 @@ TEST(4_NetlistSubcircuits) nl->add_device_class (dc); db::Circuit *c1 = new db::Circuit (); + EXPECT_EQ (c1->netlist (), 0); c1->set_name ("c1"); c1->add_pin (db::Pin ("c1p1")); c1->add_pin (db::Pin ("c1p2")); nl->add_circuit (c1); + EXPECT_EQ (c1->netlist (), nl.get ()); db::Circuit *c2 = new db::Circuit (); c2->set_name ("c2"); @@ -296,6 +301,8 @@ TEST(4_NetlistSubcircuits) db::Netlist nl2 = *nl; nl.reset (0); + EXPECT_EQ (nl2.begin_circuits ()->netlist (), &nl2); + EXPECT_EQ (netlist2string (nl2), "[c1]\n" "+c1p1,c2:c2p1\n" From e51a3a9ed9fcedade93dbf95bbc4a765a2476617 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Dec 2018 00:29:44 +0100 Subject: [PATCH 096/335] Dual netlist representation (nets attached to pins and ports). --- src/db/db/dbNetlist.cc | 196 +++++++++++++++++++++++++++- src/db/db/dbNetlist.h | 106 ++++++++++++++- src/db/unit_tests/dbNetlistTests.cc | 112 +++++++++++++++- 3 files changed, 403 insertions(+), 11 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index e18052b1a..12bdc12fe 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -43,17 +43,66 @@ Pin::Pin (const std::string &name) // -------------------------------------------------------------------------------- // Device class implementation +Device::Device () +{ + // .. nothing yet .. +} + Device::Device (DeviceClass *device_class, const std::string &name) : m_device_class (device_class), m_name (name) { // .. nothing yet .. } +Device::Device (const Device &other) +{ + operator= (other); +} + +Device &Device::operator= (const Device &other) +{ + if (this != &other) { + m_name = other.m_name; + m_device_class = other.m_device_class; + m_nets_per_port.clear (); + } + return *this; +} + void Device::set_name (const std::string &n) { m_name = n; } +void Device::clear_nets_per_port () +{ + m_nets_per_port.clear (); +} + +void Device::reserve_nets_per_port () +{ + if (m_device_class.get ()) { + m_nets_per_port.clear (); + m_nets_per_port.resize (m_device_class->port_definitions ().size (), 0); + } +} + +void Device::set_net_for_port (size_t port_id, const Net *net) +{ + if (port_id < m_nets_per_port.size ()) { + m_nets_per_port [port_id] = net; + } +} + +const Net *Device::net_for_port (size_t port_id) const +{ + if (port_id < m_nets_per_port.size ()) { + return m_nets_per_port [port_id]; + } else { + return 0; + } +} + // -------------------------------------------------------------------------------- // SubCircuit class implementation @@ -68,6 +117,22 @@ SubCircuit::SubCircuit (Circuit *circuit, const std::string &name) // .. nothing yet .. } +SubCircuit::SubCircuit (const SubCircuit &other) +{ + operator= (other); +} + +SubCircuit &SubCircuit::operator= (const SubCircuit &other) +{ + if (this != &other) { + m_name = other.m_name; + m_circuit = other.m_circuit; + m_trans = other.m_trans; + m_nets_per_pin.clear (); + } + return *this; +} + void SubCircuit::set_name (const std::string &n) { m_name = n; @@ -78,6 +143,35 @@ void SubCircuit::set_trans (const db::DCplxTrans &t) m_trans = t; } +void SubCircuit::clear_nets_per_pin () +{ + m_nets_per_pin.clear (); +} + +void SubCircuit::reserve_nets_per_pin () +{ + if (m_circuit.get ()) { + m_nets_per_pin.clear (); + m_nets_per_pin.resize (m_circuit->pin_count (), 0); + } +} + +void SubCircuit::set_net_for_pin (size_t pin_id, const Net *net) +{ + if (pin_id < m_nets_per_pin.size ()) { + m_nets_per_pin [pin_id] = net; + } +} + +const Net *SubCircuit::net_for_pin (size_t pin_id) const +{ + if (pin_id < m_nets_per_pin.size ()) { + return m_nets_per_pin [pin_id]; + } else { + return 0; + } +} + // -------------------------------------------------------------------------------- // NetPortRef class implementation @@ -188,11 +282,17 @@ void Net::set_cluster_id (size_t ci) void Net::add_pin (const NetPinRef &pin) { m_pins.push_back (pin); + if (mp_circuit) { + mp_circuit->invalidate_nets_per_pin (); + } } void Net::add_port (const NetPortRef &port) { m_ports.push_back (port); + if (mp_circuit) { + mp_circuit->invalidate_nets_per_pin (); + } } void Net::translate_devices (const std::map &map) @@ -224,13 +324,13 @@ void Net::set_circuit (Circuit *circuit) // Circuit class implementation Circuit::Circuit () - : mp_netlist (0) + : mp_netlist (0), m_nets_per_pin_valid (false) { // .. nothing yet .. } Circuit::Circuit (const Circuit &other) - : mp_netlist (0) + : mp_netlist (0), m_nets_per_pin_valid (false) { operator= (other); } @@ -240,6 +340,9 @@ Circuit &Circuit::operator= (const Circuit &other) if (this != &other) { m_name = other.m_name; + m_nets_per_pin_valid = false; + m_nets_per_pin.clear (); + invalidate_nets_per_pin (); for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { add_pin (*i); @@ -291,6 +394,7 @@ void Circuit::clear () m_devices.clear (); m_nets.clear (); m_sub_circuits.clear (); + invalidate_nets_per_pin (); } void Circuit::set_name (const std::string &name) @@ -307,37 +411,44 @@ void Circuit::add_pin (const Pin &pin) { m_pins.push_back (pin); m_pins.back ().set_id (m_pins.size () - 1); + invalidate_nets_per_pin (); } void Circuit::add_net (Net *net) { m_nets.push_back (net); net->set_circuit (this); + invalidate_nets_per_pin (); } void Circuit::remove_net (Net *net) { m_nets.erase (net); + invalidate_nets_per_pin (); } void Circuit::add_device (Device *device) { m_devices.push_back (device); + invalidate_nets_per_pin (); } void Circuit::remove_device (Device *device) { m_devices.erase (device); + invalidate_nets_per_pin (); } void Circuit::add_sub_circuit (SubCircuit *sub_circuit) { m_sub_circuits.push_back (sub_circuit); + invalidate_nets_per_pin (); } void Circuit::remove_sub_circuit (SubCircuit *sub_circuit) { m_sub_circuits.erase (sub_circuit); + invalidate_nets_per_pin (); } void Circuit::translate_circuits (const std::map &map) @@ -358,6 +469,87 @@ void Circuit::translate_device_classes (const std::map (this)->validate_nets_per_pin (); + } + if (pin_id >= m_nets_per_pin.size ()) { + return 0; + } else { + return m_nets_per_pin [pin_id]; + } +} + +const Net *Circuit::net_for_pin (const SubCircuit *sub_circuit, size_t pin_id) const +{ + if (! m_nets_per_pin_valid) { + const_cast (this)->validate_nets_per_pin (); + } + return sub_circuit->net_for_pin (pin_id); +} + +const Net *Circuit::net_for_port (const Device *device, size_t port_id) const +{ + if (! m_nets_per_pin_valid) { + const_cast (this)->validate_nets_per_pin (); + } + return device->net_for_port (port_id); +} + +void Circuit::invalidate_nets_per_pin () +{ + if (m_nets_per_pin_valid) { + + m_nets_per_pin_valid = false; + m_nets_per_pin.clear (); + + for (sub_circuit_iterator i = begin_sub_circuits (); i != end_sub_circuits (); ++i) { + i->clear_nets_per_pin (); + } + + for (device_iterator i = begin_devices (); i != end_devices (); ++i) { + i->clear_nets_per_port (); + } + + } +} + +void Circuit::validate_nets_per_pin () +{ + m_nets_per_pin.clear (); + m_nets_per_pin.resize (m_pins.size (), 0); + + for (sub_circuit_iterator i = begin_sub_circuits (); i != end_sub_circuits (); ++i) { + i->reserve_nets_per_pin (); + } + + for (device_iterator i = begin_devices (); i != end_devices (); ++i) { + i->reserve_nets_per_port (); + } + + for (net_iterator n = begin_nets (); n != end_nets (); ++n) { + + for (Net::const_pin_iterator p = n->begin_pins (); p != n->end_pins (); ++p) { + size_t id = p->pin_id (); + if (p->subcircuit () == 0) { + if (id < m_nets_per_pin.size ()) { + m_nets_per_pin [id] = n.operator-> (); + } + } else { + (const_cast (p->subcircuit ()))->set_net_for_pin (id, n.operator-> ()); + } + } + + for (Net::const_port_iterator p = n->begin_ports (); p != n->end_ports (); ++p) { + (const_cast (p->device ()))->set_net_for_port (p->port_id (), n.operator-> ()); + } + + } + + m_nets_per_pin_valid = true; +} + // -------------------------------------------------------------------------------- // DeviceClass class implementation diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 8935dca49..c52bd1db1 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -42,6 +42,7 @@ class Device; class DeviceClass; class DevicePortDefinition; class Netlist; +class Net; /** * @brief The definition of a pin of a circuit * @@ -102,11 +103,28 @@ class DB_PUBLIC Device : public tl::Object { public: + typedef tl::vector connected_net_list; + + /** + * @brief Default constructor + */ + Device (); + /** * @brief The constructor */ Device (DeviceClass *device_class, const std::string &name = std::string ()); + /** + * @brief Copy constructor + */ + Device (const Device &other); + + /** + * @brief Assignment + */ + Device &operator= (const Device &other); + /** * @brief Gets the device class */ @@ -133,6 +151,12 @@ private: tl::weak_ptr m_device_class; std::string m_name; + mutable connected_net_list m_nets_per_port; + + void clear_nets_per_port (); + void reserve_nets_per_port (); + void set_net_for_port (size_t port_id, const Net *net); + const Net *net_for_port (size_t port_id) const; /** * @brief Sets the device class @@ -152,11 +176,23 @@ class DB_PUBLIC SubCircuit : public tl::Object { public: + typedef tl::vector connected_net_list; + /** * @brief Default constructor */ SubCircuit (); + /** + * @brief Copy constructor + */ + SubCircuit (const SubCircuit &other); + + /** + * @brief Assignment + */ + SubCircuit &operator= (const SubCircuit &other); + /** * @brief Creates a subcircuit reference to the given circuit */ @@ -216,6 +252,12 @@ private: tl::weak_ptr m_circuit; std::string m_name; db::DCplxTrans m_trans; + mutable connected_net_list m_nets_per_pin; + + void clear_nets_per_pin (); + void reserve_nets_per_pin (); + void set_net_for_pin (size_t pin_id, const Net *net); + const Net *net_for_pin (size_t pin_id) const; /** * @brief Sets the circuit reference @@ -524,6 +566,7 @@ public: typedef tl::shared_collection sub_circuit_list; typedef sub_circuit_list::const_iterator const_sub_circuit_iterator; typedef sub_circuit_list::iterator sub_circuit_iterator; + typedef tl::vector connected_net_list; /** * @brief Constructor @@ -600,11 +643,6 @@ public: */ void add_pin (const Pin &pin); - /** - * @brief Deletes a pin from the circuit - */ - void remove_pin (Pin *pin); - /** * @brief Begin iterator for the pins of the circuit (non-const version) */ @@ -621,6 +659,14 @@ public: return m_pins.end (); } + /** + * @brief Gets the pin count + */ + size_t pin_count () const + { + return m_pins.size (); + } + /** * @brief Gets the pin by ID (the ID is basically the index) */ @@ -774,8 +820,52 @@ public: return m_sub_circuits.end (); } + /** + * @brief Gets the connected net for a pin with the given id + * + * Returns 0 if the pin is not connected to a net. + */ + const Net *net_for_pin (size_t pin_id) const; + + /** + * @brief Gets the connected net for a pin with the given id (non-const version) + * + * Returns 0 if the pin is not connected to a net. + */ + Net *net_for_pin (size_t pin_id) + { + return const_cast (((const Circuit *) this)->net_for_pin (pin_id)); + } + + /** + * @brief Gets the net for a given pin of a subcircuit + */ + const Net *net_for_pin (const SubCircuit *sub_circuit, size_t pin_id) const; + + /** + * @brief Gets the net for a given pin of a subcircuit (non-const version) + */ + Net *net_for_pin (SubCircuit *sub_circuit, size_t pin_id) + { + return const_cast (((const Circuit *) this)->net_for_pin (sub_circuit, pin_id)); + } + + /** + * @brief Gets the net for a given port of a device + */ + const Net *net_for_port (const Device *device, size_t port_id) const; + + /** + * @brief Gets the net for a given port of a device (non-const version) + */ + Net *net_for_port (Device *device, size_t port_id) + { + return const_cast (((const Circuit *) this)->net_for_port (device, port_id)); + } + private: friend class Netlist; + friend class Net; std::string m_name; db::cell_index_type m_cell_index; @@ -784,10 +874,14 @@ private: device_list m_devices; sub_circuit_list m_sub_circuits; Netlist *mp_netlist; + connected_net_list m_nets_per_pin; + bool m_nets_per_pin_valid; void translate_circuits (const std::map &map); - void translate_device_classes (const std::map &map); + void translate_device_classes (const std::map &map); void set_netlist (Netlist *netlist); + void invalidate_nets_per_pin (); + void validate_nets_per_pin (); }; /** diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index c2c37ba3a..cb95eff06 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -164,6 +164,61 @@ static std::string nets2string (const db::Circuit &c) return res; } +// dual form of netlist +static std::string netlist2 (const db::Circuit &c) +{ + std::string res; + + std::string pins; + for (db::Circuit::const_pin_iterator p = c.begin_pins (); p != c.end_pins (); ++p) { + if (! pins.empty ()) { + pins += ","; + } + const db::Net *net = c.net_for_pin (p->id ()); + pins += p->name (); + pins += "="; + pins += net ? net->name () : std::string ("(null)"); + } + + res += c.name () + ":" + pins + "\n"; + + for (db::Circuit::const_device_iterator d = c.begin_devices (); d != c.end_devices (); ++d) { + if (! d->device_class ()) { + continue; + } + pins.clear (); + for (size_t i = 0; i < d->device_class ()->port_definitions ().size (); ++i) { + if (! pins.empty ()) { + pins += ","; + } + const db::Net *net = c.net_for_port (d.operator-> (), i); + pins += d->device_class ()->port_definitions () [i].name (); + pins += "="; + pins += net ? net->name () : std::string ("(null)"); + } + res += " D" + d->name () + ":" + pins + "\n"; + } + + for (db::Circuit::const_sub_circuit_iterator s = c.begin_sub_circuits (); s != c.end_sub_circuits (); ++s) { + if (! s->circuit ()) { + continue; + } + pins.clear (); + for (size_t i = 0; i < s->circuit ()->pin_count (); ++i) { + if (! pins.empty ()) { + pins += ","; + } + pins += s->circuit ()->pin_by_id (i)->name (); + pins += "="; + const db::Net *net = c.net_for_pin (s.operator-> (), i); + pins += net ? net->name () : std::string ("(null)"); + } + res += " X" + s->name () + ":" + pins + "\n"; + } + + return res; +} + TEST(4_CircuitDevices) { db::GenericDeviceClass dc1; @@ -178,6 +233,7 @@ TEST(4_CircuitDevices) dc2.add_port_definition (db::DevicePortDefinition ("B", "")); std::auto_ptr c (new db::Circuit ()); + c->set_name ("c"); db::Device *d1 = new db::Device (&dc1, "d1"); db::Device *d2a = new db::Device (&dc2, "d2a"); db::Device *d2b = new db::Device (&dc2, "d2b"); @@ -186,6 +242,7 @@ TEST(4_CircuitDevices) c->add_device (d2b); db::Net *n1 = new db::Net (); + n1->set_name ("n1"); EXPECT_EQ (n1->circuit (), 0); c->add_net (n1); n1->add_port (db::NetPortRef (d1, 0)); @@ -193,12 +250,14 @@ TEST(4_CircuitDevices) EXPECT_EQ (n1->circuit (), c.get ()); db::Net *n2 = new db::Net (); + n2->set_name ("n2"); c->add_net (n2); n2->add_port (db::NetPortRef (d1, 1)); n2->add_port (db::NetPortRef (d2a, 1)); n2->add_port (db::NetPortRef (d2b, 0)); db::Net *n3 = new db::Net (); + n3->set_name ("n3"); c->add_net (n3); n3->add_port (db::NetPortRef (d1, 2)); n3->add_port (db::NetPortRef (d2b, 1)); @@ -209,6 +268,13 @@ TEST(4_CircuitDevices) "d1:D,d2b:B\n" ); + EXPECT_EQ (netlist2 (*c), + "c:\n" + " Dd1:S=n1,G=n2,D=n3\n" + " Dd2a:A=n1,B=n2\n" + " Dd2b:A=n2,B=n3\n" + ); + db::Circuit cc = *c; c.reset (0); EXPECT_EQ (cc.begin_nets ()->circuit (), &cc); @@ -218,9 +284,16 @@ TEST(4_CircuitDevices) "d1:G,d2a:B,d2b:A\n" "d1:D,d2b:B\n" ); + + EXPECT_EQ (netlist2 (cc), + "c:\n" + " Dd1:S=n1,G=n2,D=n3\n" + " Dd2a:A=n1,B=n2\n" + " Dd2b:A=n2,B=n3\n" + ); } -static std::string netlist2string (const db::Netlist &nl) +static std::string nl2string (const db::Netlist &nl) { std::string res; for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { @@ -230,6 +303,16 @@ static std::string netlist2string (const db::Netlist &nl) return res; } +// dual form of netlist +static std::string netlist2 (const db::Netlist &nl) +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + res += netlist2 (*c); + } + return res; +} + TEST(4_NetlistSubcircuits) { std::auto_ptr nl (new db::Netlist ()); @@ -258,37 +341,44 @@ TEST(4_NetlistSubcircuits) c2->add_device (d); db::SubCircuit *sc1 = new db::SubCircuit (c2); + sc1->set_name ("sc1"); c1->add_sub_circuit (sc1); db::SubCircuit *sc2 = new db::SubCircuit (c2); + sc2->set_name ("sc2"); c1->add_sub_circuit (sc2); db::Net *n2a = new db::Net (); + n2a->set_name ("n2a"); n2a->add_pin (db::NetPinRef (0)); n2a->add_port (db::NetPortRef (d, 0)); c2->add_net (n2a); db::Net *n2b = new db::Net (); + n2b->set_name ("n2b"); n2b->add_port (db::NetPortRef (d, 1)); n2b->add_pin (db::NetPinRef (1)); c2->add_net (n2b); db::Net *n1a = new db::Net (); + n1a->set_name ("n1a"); n1a->add_pin (db::NetPinRef (0)); n1a->add_pin (db::NetPinRef (0, sc1)); c1->add_net (n1a); db::Net *n1b = new db::Net (); + n1b->set_name ("n1b"); n1b->add_pin (db::NetPinRef (1, sc1)); n1b->add_pin (db::NetPinRef (0, sc2)); c1->add_net (n1b); db::Net *n1c = new db::Net (); + n1c->set_name ("n1c"); n1c->add_pin (db::NetPinRef (1, sc2)); n1c->add_pin (db::NetPinRef (1)); c1->add_net (n1c); - EXPECT_EQ (netlist2string (*nl), + EXPECT_EQ (nl2string (*nl), "[c1]\n" "+c1p1,c2:c2p1\n" "c2:c2p2,c2:c2p1\n" @@ -298,12 +388,20 @@ TEST(4_NetlistSubcircuits) "D:B,+c2p2\n" ); + EXPECT_EQ (netlist2 (*nl), + "c1:c1p1=n1a,c1p2=n1c\n" + " Xsc1:c2p1=n1a,c2p2=n1b\n" + " Xsc2:c2p1=n1b,c2p2=n1c\n" + "c2:c2p1=n2a,c2p2=n2b\n" + " DD:A=n2a,B=n2b\n" + ); + db::Netlist nl2 = *nl; nl.reset (0); EXPECT_EQ (nl2.begin_circuits ()->netlist (), &nl2); - EXPECT_EQ (netlist2string (nl2), + EXPECT_EQ (nl2string (nl2), "[c1]\n" "+c1p1,c2:c2p1\n" "c2:c2p2,c2:c2p1\n" @@ -312,6 +410,14 @@ TEST(4_NetlistSubcircuits) "D:A,+c2p1\n" "D:B,+c2p2\n" ); + + EXPECT_EQ (netlist2 (nl2), + "c1:c1p1=n1a,c1p2=n1c\n" + " Xsc1:c2p1=n1a,c2p2=n1b\n" + " Xsc2:c2p1=n1b,c2p2=n1c\n" + "c2:c2p1=n2a,c2p2=n2b\n" + " DD:A=n2a,B=n2b\n" + ); } TEST(5_SubCircuit) From 9a9482d7c74e7436e6b7965bfdd3f185c4b570dc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Dec 2018 23:12:45 +0100 Subject: [PATCH 097/335] Added netlist editing features. --- src/db/db/dbNetlist.cc | 379 ++++++++------- src/db/db/dbNetlist.h | 692 +++++++++++++++++----------- src/db/unit_tests/dbNetlistTests.cc | 234 +++++++++- 3 files changed, 861 insertions(+), 444 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 12bdc12fe..e1866f114 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -48,6 +48,15 @@ Device::Device () // .. nothing yet .. } +Device::~Device () +{ + for (std::vector::const_iterator p = m_port_refs.begin (); p != m_port_refs.end (); ++p) { + if (*p != Net::port_iterator () && (*p)->net ()) { + (*p)->net ()->erase_port (*p); + } + } +} + Device::Device (DeviceClass *device_class, const std::string &name) : m_device_class (device_class), m_name (name) { @@ -64,7 +73,6 @@ Device &Device::operator= (const Device &other) if (this != &other) { m_name = other.m_name; m_device_class = other.m_device_class; - m_nets_per_port.clear (); } return *this; } @@ -74,32 +82,41 @@ void Device::set_name (const std::string &n) m_name = n; } -void Device::clear_nets_per_port () +void Device::set_port_ref_for_port (size_t port_id, Net::port_iterator iter) { - m_nets_per_port.clear (); -} - -void Device::reserve_nets_per_port () -{ - if (m_device_class.get ()) { - m_nets_per_port.clear (); - m_nets_per_port.resize (m_device_class->port_definitions ().size (), 0); - } -} - -void Device::set_net_for_port (size_t port_id, const Net *net) -{ - if (port_id < m_nets_per_port.size ()) { - m_nets_per_port [port_id] = net; + if (m_port_refs.size () < port_id + 1) { + m_port_refs.resize (port_id + 1, Net::port_iterator ()); } + m_port_refs [port_id] = iter; } const Net *Device::net_for_port (size_t port_id) const { - if (port_id < m_nets_per_port.size ()) { - return m_nets_per_port [port_id]; - } else { - return 0; + if (port_id < m_port_refs.size ()) { + Net::port_iterator p = m_port_refs [port_id]; + if (p != Net::port_iterator ()) { + return p->net (); + } + } + return 0; +} + +void Device::connect_port (size_t port_id, Net *net) +{ + if (net_for_port (port_id) == net) { + return; + } + + if (port_id < m_port_refs.size ()) { + Net::port_iterator p = m_port_refs [port_id]; + if (p != Net::port_iterator () && p->net ()) { + p->net ()->erase_port (p); + } + m_port_refs [port_id] = Net::port_iterator (); + } + + if (net) { + net->add_port (NetPortRef (this, port_id)); } } @@ -111,6 +128,15 @@ SubCircuit::SubCircuit () // .. nothing yet .. } +SubCircuit::~SubCircuit() +{ + for (std::vector::const_iterator p = m_pin_refs.begin (); p != m_pin_refs.end (); ++p) { + if (*p != Net::pin_iterator () && (*p)->net ()) { + (*p)->net ()->erase_pin (*p); + } + } +} + SubCircuit::SubCircuit (Circuit *circuit, const std::string &name) : m_circuit (circuit), m_name (name) { @@ -128,7 +154,6 @@ SubCircuit &SubCircuit::operator= (const SubCircuit &other) m_name = other.m_name; m_circuit = other.m_circuit; m_trans = other.m_trans; - m_nets_per_pin.clear (); } return *this; } @@ -143,32 +168,41 @@ void SubCircuit::set_trans (const db::DCplxTrans &t) m_trans = t; } -void SubCircuit::clear_nets_per_pin () +void SubCircuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter) { - m_nets_per_pin.clear (); -} - -void SubCircuit::reserve_nets_per_pin () -{ - if (m_circuit.get ()) { - m_nets_per_pin.clear (); - m_nets_per_pin.resize (m_circuit->pin_count (), 0); - } -} - -void SubCircuit::set_net_for_pin (size_t pin_id, const Net *net) -{ - if (pin_id < m_nets_per_pin.size ()) { - m_nets_per_pin [pin_id] = net; + if (m_pin_refs.size () < pin_id + 1) { + m_pin_refs.resize (pin_id + 1, Net::pin_iterator ()); } + m_pin_refs [pin_id] = iter; } const Net *SubCircuit::net_for_pin (size_t pin_id) const { - if (pin_id < m_nets_per_pin.size ()) { - return m_nets_per_pin [pin_id]; - } else { - return 0; + if (pin_id < m_pin_refs.size ()) { + Net::pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::pin_iterator ()) { + return p->net (); + } + } + return 0; +} + +void SubCircuit::connect_pin (size_t pin_id, Net *net) +{ + if (net_for_pin (pin_id) == net) { + return; + } + + if (pin_id < m_pin_refs.size ()) { + Net::pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::pin_iterator () && p->net ()) { + p->net ()->erase_pin (p); + } + m_pin_refs [pin_id] = Net::pin_iterator (); + } + + if (net) { + net->add_pin (NetPinRef (this, pin_id)); } } @@ -176,16 +210,32 @@ const Net *SubCircuit::net_for_pin (size_t pin_id) const // NetPortRef class implementation NetPortRef::NetPortRef () + : m_port_id (0), mp_device (0), mp_net (0) { // .. nothing yet .. } NetPortRef::NetPortRef (Device *device, size_t port_id) - : m_device (device), m_port_id (port_id) + : m_port_id (port_id), mp_device (device), mp_net (0) { // .. nothing yet .. } +NetPortRef::NetPortRef (const NetPortRef &other) + : m_port_id (other.m_port_id), mp_device (other.mp_device), mp_net (0) +{ + // .. nothing yet .. +} + +NetPortRef &NetPortRef::operator= (const NetPortRef &other) +{ + if (this != &other) { + m_port_id = other.m_port_id; + mp_device = other.mp_device; + } + return *this; +} + const DevicePortDefinition * NetPortRef::port_def () const { @@ -200,38 +250,53 @@ NetPortRef::port_def () const const DeviceClass * NetPortRef::device_class () const { - const Device *device = m_device.get (); - return device ? device->device_class () : 0; + return mp_device ? mp_device->device_class () : 0; } // -------------------------------------------------------------------------------- // NetPinRef class implementation NetPinRef::NetPinRef () + : m_pin_id (0), mp_subcircuit (0), mp_net (0) { // .. nothing yet .. } NetPinRef::NetPinRef (size_t pin_id) - : m_pin_id (pin_id) + : m_pin_id (pin_id), mp_subcircuit (0), mp_net (0) { // .. nothing yet .. } -NetPinRef::NetPinRef (size_t pin_id, SubCircuit *circuit) - : m_pin_id (pin_id), m_subcircuit (circuit) +NetPinRef::NetPinRef (SubCircuit *circuit, size_t pin_id) + : m_pin_id (pin_id), mp_subcircuit (circuit), mp_net (0) { // .. nothing yet .. } +NetPinRef::NetPinRef (const NetPinRef &other) + : m_pin_id (other.m_pin_id), mp_subcircuit (other.mp_subcircuit), mp_net (0) +{ + // .. nothing yet .. +} + +NetPinRef &NetPinRef::operator= (const NetPinRef &other) +{ + if (this != &other) { + m_pin_id = other.m_pin_id; + mp_subcircuit = other.mp_subcircuit; + } + return *this; +} + const Pin *NetPinRef::pin (const db::Circuit *c) const { - if (! m_subcircuit.get ()) { + if (! mp_subcircuit) { tl_assert (c != 0); return c->pin_by_id (m_pin_id); } else { - tl_assert (m_subcircuit->circuit () != 0); - return m_subcircuit->circuit ()->pin_by_id (m_pin_id); + tl_assert (mp_subcircuit->circuit () != 0); + return mp_subcircuit->circuit ()->pin_by_id (m_pin_id); } } @@ -253,20 +318,41 @@ Net::Net (const Net &other) Net &Net::operator= (const Net &other) { if (this != &other) { + + clear (); + m_name = other.m_name; - m_pins = other.m_pins; - m_ports = other.m_ports; m_cluster_id = other.m_cluster_id; + + for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { + add_pin (*i); + } + + for (const_port_iterator i = other.begin_ports (); i != other.end_ports (); ++i) { + add_port (*i); + } + } return *this; } +Net::~Net () +{ + clear (); +} + void Net::clear () { m_name.clear (); - m_ports.clear (); - m_pins.clear (); m_cluster_id = 0; + + while (! m_ports.empty ()) { + erase_port (begin_ports ()); + } + + while (! m_pins.empty ()) { + erase_pin (begin_pins ()); + } } void Net::set_name (const std::string &name) @@ -282,37 +368,46 @@ void Net::set_cluster_id (size_t ci) void Net::add_pin (const NetPinRef &pin) { m_pins.push_back (pin); - if (mp_circuit) { - mp_circuit->invalidate_nets_per_pin (); + NetPinRef &new_pin = m_pins.back (); + new_pin.set_net (this); + + if (! pin.subcircuit ()) { + if (mp_circuit) { + mp_circuit->set_pin_ref_for_pin (new_pin.pin_id (), --m_pins.end ()); + } + } else { + new_pin.subcircuit ()->set_pin_ref_for_pin (new_pin.pin_id (), --m_pins.end ()); } } +void Net::erase_pin (pin_iterator iter) +{ + if (iter->subcircuit ()) { + iter->subcircuit ()->set_pin_ref_for_pin (iter->pin_id (), pin_iterator ()); + } else if (mp_circuit) { + mp_circuit->set_pin_ref_for_pin (iter->pin_id (), pin_iterator ()); + } + m_pins.erase (iter); +} + void Net::add_port (const NetPortRef &port) { + if (! port.device ()) { + return; + } + m_ports.push_back (port); - if (mp_circuit) { - mp_circuit->invalidate_nets_per_pin (); - } + NetPortRef &new_port = m_ports.back (); + new_port.set_net (this); + new_port.device ()->set_port_ref_for_port (new_port.port_id (), --m_ports.end ()); } -void Net::translate_devices (const std::map &map) +void Net::erase_port (port_iterator iter) { - for (port_list::iterator i = m_ports.begin (); i != m_ports.end (); ++i) { - std::map::const_iterator m = map.find (i->device ()); - tl_assert (m != map.end ()); - i->set_device (m->second); - } -} - -void Net::translate_subcircuits (const std::map &map) -{ - for (pin_list::iterator i = m_pins.begin (); i != m_pins.end (); ++i) { - if (i->subcircuit ()) { - std::map::const_iterator m = map.find (i->subcircuit ()); - tl_assert (m != map.end ()); - i->set_subcircuit (m->second); - } + if (iter->device ()) { + iter->device ()->set_port_ref_for_port (iter->port_id (), port_iterator ()); } + m_ports.erase (iter); } void Net::set_circuit (Circuit *circuit) @@ -324,13 +419,13 @@ void Net::set_circuit (Circuit *circuit) // Circuit class implementation Circuit::Circuit () - : mp_netlist (0), m_nets_per_pin_valid (false) + : mp_netlist (0) { // .. nothing yet .. } Circuit::Circuit (const Circuit &other) - : mp_netlist (0), m_nets_per_pin_valid (false) + : mp_netlist (0) { operator= (other); } @@ -340,9 +435,6 @@ Circuit &Circuit::operator= (const Circuit &other) if (this != &other) { m_name = other.m_name; - m_nets_per_pin_valid = false; - m_nets_per_pin.clear (); - invalidate_nets_per_pin (); for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { add_pin (*i); @@ -363,13 +455,33 @@ Circuit &Circuit::operator= (const Circuit &other) } for (const_net_iterator i = other.begin_nets (); i != other.end_nets (); ++i) { - Net *n = new Net (*i); - n->translate_devices (device_table); - n->translate_subcircuits (sc_table); + + // translate the net + Net *n = new Net (); + n->set_cluster_id (i->cluster_id ()); + n->set_name (i->name ()); add_net (n); + + for (Net::const_port_iterator p = i->begin_ports (); p != i->end_ports (); ++p) { + std::map::const_iterator m = device_table.find (p->device ()); + tl_assert (m != device_table.end ()); + n->add_port (NetPortRef (m->second, p->port_id ())); + } + + for (Net::const_pin_iterator p = i->begin_pins (); p != i->end_pins (); ++p) { + if (! p->subcircuit ()) { + n->add_pin (NetPinRef (p->pin_id ())); + } else { + std::map::const_iterator m = sc_table.find (p->subcircuit ()); + tl_assert (m != sc_table.end ()); + n->add_pin (NetPinRef (m->second, p->pin_id ())); + } + } + } } + return *this; } @@ -394,7 +506,6 @@ void Circuit::clear () m_devices.clear (); m_nets.clear (); m_sub_circuits.clear (); - invalidate_nets_per_pin (); } void Circuit::set_name (const std::string &name) @@ -411,44 +522,37 @@ void Circuit::add_pin (const Pin &pin) { m_pins.push_back (pin); m_pins.back ().set_id (m_pins.size () - 1); - invalidate_nets_per_pin (); } void Circuit::add_net (Net *net) { m_nets.push_back (net); net->set_circuit (this); - invalidate_nets_per_pin (); } void Circuit::remove_net (Net *net) { m_nets.erase (net); - invalidate_nets_per_pin (); } void Circuit::add_device (Device *device) { m_devices.push_back (device); - invalidate_nets_per_pin (); } void Circuit::remove_device (Device *device) { m_devices.erase (device); - invalidate_nets_per_pin (); } void Circuit::add_sub_circuit (SubCircuit *sub_circuit) { m_sub_circuits.push_back (sub_circuit); - invalidate_nets_per_pin (); } void Circuit::remove_sub_circuit (SubCircuit *sub_circuit) { m_sub_circuits.erase (sub_circuit); - invalidate_nets_per_pin (); } void Circuit::translate_circuits (const std::map &map) @@ -469,85 +573,42 @@ void Circuit::translate_device_classes (const std::map (this)->validate_nets_per_pin (); - } - if (pin_id >= m_nets_per_pin.size ()) { - return 0; - } else { - return m_nets_per_pin [pin_id]; + if (pin_id < m_pin_refs.size ()) { + Net::pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::pin_iterator ()) { + return p->net (); + } } + return 0; } -const Net *Circuit::net_for_pin (const SubCircuit *sub_circuit, size_t pin_id) const +void Circuit::connect_pin (size_t pin_id, Net *net) { - if (! m_nets_per_pin_valid) { - const_cast (this)->validate_nets_per_pin (); + if (net_for_pin (pin_id) == net) { + return; } - return sub_circuit->net_for_pin (pin_id); -} -const Net *Circuit::net_for_port (const Device *device, size_t port_id) const -{ - if (! m_nets_per_pin_valid) { - const_cast (this)->validate_nets_per_pin (); - } - return device->net_for_port (port_id); -} - -void Circuit::invalidate_nets_per_pin () -{ - if (m_nets_per_pin_valid) { - - m_nets_per_pin_valid = false; - m_nets_per_pin.clear (); - - for (sub_circuit_iterator i = begin_sub_circuits (); i != end_sub_circuits (); ++i) { - i->clear_nets_per_pin (); + if (pin_id < m_pin_refs.size ()) { + Net::pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::pin_iterator () && p->net ()) { + p->net ()->erase_pin (p); } - - for (device_iterator i = begin_devices (); i != end_devices (); ++i) { - i->clear_nets_per_port (); - } - - } -} - -void Circuit::validate_nets_per_pin () -{ - m_nets_per_pin.clear (); - m_nets_per_pin.resize (m_pins.size (), 0); - - for (sub_circuit_iterator i = begin_sub_circuits (); i != end_sub_circuits (); ++i) { - i->reserve_nets_per_pin (); + m_pin_refs [pin_id] = Net::pin_iterator (); } - for (device_iterator i = begin_devices (); i != end_devices (); ++i) { - i->reserve_nets_per_port (); + if (net) { + net->add_pin (NetPinRef (pin_id)); } - - for (net_iterator n = begin_nets (); n != end_nets (); ++n) { - - for (Net::const_pin_iterator p = n->begin_pins (); p != n->end_pins (); ++p) { - size_t id = p->pin_id (); - if (p->subcircuit () == 0) { - if (id < m_nets_per_pin.size ()) { - m_nets_per_pin [id] = n.operator-> (); - } - } else { - (const_cast (p->subcircuit ()))->set_net_for_pin (id, n.operator-> ()); - } - } - - for (Net::const_port_iterator p = n->begin_ports (); p != n->end_ports (); ++p) { - (const_cast (p->device ()))->set_net_for_port (p->port_id (), n.operator-> ()); - } - - } - - m_nets_per_pin_valid = true; } // -------------------------------------------------------------------------------- diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index c52bd1db1..d31a294f4 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -38,235 +38,13 @@ namespace db { class Circuit; +class SubCircuit; +class Pin; class Device; class DeviceClass; class DevicePortDefinition; class Netlist; class Net; -/** - * @brief The definition of a pin of a circuit - * - * A pin is some place other nets can connect to a circuit. - */ -class DB_PUBLIC Pin -{ -public: - /** - * @brief Default constructor - */ - Pin (); - - /** - * @brief Creates a pin with the given name. - */ - Pin (const std::string &name); - - /** - * @brief Gets the name of the pin - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief Gets the ID of the pin (only pins inside circuits have valid ID's) - */ - size_t id () const - { - return m_id; - } - -private: - friend class Circuit; - - tl::weak_ptr m_circuit; - std::string m_name; - size_t m_id; - - void set_id (size_t id) - { - m_id = id; - } -}; - -/** - * @brief An actual device - * - * This class represents the incarnation of a specific device. - * The device has a class which specifies a type. This class - * is intended for subclassing. - * A specific device subclass is supposed to correspond to - * a specific device class. - */ -class DB_PUBLIC Device - : public tl::Object -{ -public: - typedef tl::vector connected_net_list; - - /** - * @brief Default constructor - */ - Device (); - - /** - * @brief The constructor - */ - Device (DeviceClass *device_class, const std::string &name = std::string ()); - - /** - * @brief Copy constructor - */ - Device (const Device &other); - - /** - * @brief Assignment - */ - Device &operator= (const Device &other); - - /** - * @brief Gets the device class - */ - const DeviceClass *device_class () const - { - return m_device_class.get (); - } - - /** - * @brief Sets the name - */ - void set_name (const std::string &n); - - /** - * @brief Gets the name - */ - const std::string &name () const - { - return m_name; - } - -private: - friend class Circuit; - - tl::weak_ptr m_device_class; - std::string m_name; - mutable connected_net_list m_nets_per_port; - - void clear_nets_per_port (); - void reserve_nets_per_port (); - void set_net_for_port (size_t port_id, const Net *net); - const Net *net_for_port (size_t port_id) const; - - /** - * @brief Sets the device class - */ - void set_device_class (DeviceClass *dc) - { - m_device_class.reset (dc); - } -}; - -/** - * @brief A subcircuit of a circuit - * - * This class essentially is a reference to another circuit - */ -class DB_PUBLIC SubCircuit - : public tl::Object -{ -public: - typedef tl::vector connected_net_list; - - /** - * @brief Default constructor - */ - SubCircuit (); - - /** - * @brief Copy constructor - */ - SubCircuit (const SubCircuit &other); - - /** - * @brief Assignment - */ - SubCircuit &operator= (const SubCircuit &other); - - /** - * @brief Creates a subcircuit reference to the given circuit - */ - SubCircuit (Circuit *circuit, const std::string &name = std::string ()); - - /** - * @brief Gets the circuit the reference points to (const version) - */ - const Circuit *circuit () const - { - return m_circuit.get (); - } - - /** - * @brief Gets the circuit the reference points to (non-const version) - */ - Circuit *circuit () - { - return m_circuit.get (); - } - - /** - * @brief Sets the name of the subcircuit - * - * The name is one way to identify the subcircuit. The transformation is - * another one. - */ - void set_name (const std::string &n); - - /** - * @brief Gets the name of the subcircuit - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief Sets the transformation describing the subcircuit - * - * The transformation is a natural description of a subcircuit - * (in contrast to the name) when deriving it from a layout. - */ - void set_trans (const db::DCplxTrans &trans); - - /** - * @brief Gets the transformation describing the subcircuit - */ - const db::DCplxTrans &trans () const - { - return m_trans; - } - -private: - friend class Circuit; - - tl::weak_ptr m_circuit; - std::string m_name; - db::DCplxTrans m_trans; - mutable connected_net_list m_nets_per_pin; - - void clear_nets_per_pin (); - void reserve_nets_per_pin (); - void set_net_for_pin (size_t pin_id, const Net *net); - const Net *net_for_pin (size_t pin_id) const; - - /** - * @brief Sets the circuit reference - */ - void set_circuit (Circuit *c) - { - m_circuit.reset (c); - } -}; /** * @brief A reference to a port of a device @@ -286,12 +64,22 @@ public: */ NetPortRef (Device *device, size_t port_id); + /** + * @brief Copy constructor + */ + NetPortRef (const NetPortRef &other); + + /** + * @brief Assignment + */ + NetPortRef &operator= (const NetPortRef &other); + /** * @brief Gets the device reference */ Device *device () { - return m_device.get (); + return mp_device; } /** @@ -299,15 +87,7 @@ public: */ const Device *device () const { - return m_device.get (); - } - - /** - * @brief Sets the device reference - */ - void set_device (Device *d) - { - m_device.reset (d); + return mp_device; } /** @@ -330,9 +110,36 @@ public: */ const DeviceClass *device_class () const; + /** + * @brief Gets the net the port lives in + */ + Net *net () + { + return mp_net; + } + + /** + * @brief Gets the net the port lives in (const version) + */ + const Net *net () const + { + return mp_net; + } + private: - tl::weak_ptr m_device; + friend class Net; + size_t m_port_id; + Device *mp_device; + Net *mp_net; + + /** + * @brief Sets the net the port lives in + */ + void set_net (Net *net) + { + mp_net = net; + } }; /** @@ -358,7 +165,17 @@ public: /** * @brief Creates a pin reference to the given pin of the given subcircuit */ - NetPinRef (size_t pin_id, SubCircuit *circuit); + NetPinRef (SubCircuit *circuit, size_t pin_id); + + /** + * @brief Copy constructor + */ + NetPinRef (const NetPinRef &other); + + /** + * @brief Assignment + */ + NetPinRef &operator= (const NetPinRef &other); /** * @brief Gets the pin reference (const version) @@ -381,7 +198,7 @@ public: */ SubCircuit *subcircuit () { - return m_subcircuit.get (); + return mp_subcircuit; } /** @@ -389,20 +206,39 @@ public: */ const SubCircuit *subcircuit () const { - return m_subcircuit.get (); + return mp_subcircuit; } /** - * @brief Sets the subcircuit reference + * @brief Gets the net the pin lives in */ - void set_subcircuit (SubCircuit *s) + Net *net () { - m_subcircuit.reset (s); + return mp_net; + } + + /** + * @brief Gets the net the pin lives in (const version) + */ + const Net *net () const + { + return mp_net; } private: + friend class Net; + size_t m_pin_id; - tl::weak_ptr m_subcircuit; + SubCircuit *mp_subcircuit; + Net *mp_net; + + /** + * @brief Sets the net the port lives in + */ + void set_net (Net *net) + { + mp_net = net; + } }; /** @@ -416,12 +252,13 @@ class DB_PUBLIC Net public: typedef std::list port_list; typedef port_list::const_iterator const_port_iterator; + typedef port_list::iterator port_iterator; typedef std::list pin_list; typedef pin_list::const_iterator const_pin_iterator; + typedef pin_list::iterator pin_iterator; /** * @brief Constructor - * * Creates an empty circuit. */ Net (); @@ -431,6 +268,11 @@ public: */ Net (const Net &other); + /** + * @brief Destructor + */ + ~Net (); + /** * @brief Assignment */ @@ -493,6 +335,11 @@ public: */ void add_pin (const NetPinRef &pin); + /** + * @brief Erases the given pin from this net + */ + void erase_pin (pin_iterator iter); + /** * @brief Begin iterator for the pins of the net (const version) */ @@ -509,11 +356,32 @@ public: return m_pins.end (); } + /** + * @brief Begin iterator for the pins of the net (non-const version) + */ + pin_iterator begin_pins () + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (non-const version) + */ + pin_iterator end_pins () + { + return m_pins.end (); + } + /** * @brief Adds a port to this net */ void add_port (const NetPortRef &port); + /** + * @brief Erases the given port from this net + */ + void erase_port (port_iterator iter); + /** * @brief Begin iterator for the ports of the net (const version) */ @@ -530,6 +398,22 @@ public: return m_ports.end (); } + /** + * @brief Begin iterator for the ports of the net (non-const version) + */ + port_iterator begin_ports () + { + return m_ports.begin (); + } + + /** + * @brief End iterator for the ports of the net (non-const version) + */ + port_iterator end_ports () + { + return m_ports.end (); + } + private: friend class Circuit; @@ -539,11 +423,290 @@ private: size_t m_cluster_id; Circuit *mp_circuit; - void translate_devices (const std::map &map); - void translate_subcircuits (const std::map &map); void set_circuit (Circuit *circuit); }; +/** + * @brief The definition of a pin of a circuit + * + * A pin is some place other nets can connect to a circuit. + */ +class DB_PUBLIC Pin +{ +public: + /** + * @brief Default constructor + */ + Pin (); + + /** + * @brief Creates a pin with the given name. + */ + Pin (const std::string &name); + + /** + * @brief Gets the name of the pin + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Gets the ID of the pin (only pins inside circuits have valid ID's) + */ + size_t id () const + { + return m_id; + } + +private: + friend class Circuit; + + tl::weak_ptr m_circuit; + std::string m_name; + size_t m_id; + + void set_id (size_t id) + { + m_id = id; + } +}; + +/** + * @brief An actual device + * + * This class represents the incarnation of a specific device. + * The device has a class which specifies a type. This class + * is intended for subclassing. + * A specific device subclass is supposed to correspond to + * a specific device class. + */ +class DB_PUBLIC Device + : public tl::Object +{ +public: + /** + * @brief Default constructor + */ + Device (); + + /** + * @brief The constructor + */ + Device (DeviceClass *device_class, const std::string &name = std::string ()); + + /** + * @brief Copy constructor + */ + Device (const Device &other); + + /** + * @brief Assignment + */ + Device &operator= (const Device &other); + + /** + * @brief Destructor + */ + ~Device (); + + /** + * @brief Gets the device class + */ + const DeviceClass *device_class () const + { + return m_device_class.get (); + } + + /** + * @brief Sets the name + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Gets the net attached to a specific port + * Returns 0 if no net is attached. + */ + const Net *net_for_port (size_t port_id) const; + + /** + * @brief Gets the net attached to a specific port (non-const version) + * Returns 0 if no net is attached. + */ + Net *net_for_port (size_t port_id) + { + return const_cast (((const Device *) this)->net_for_port (port_id)); + } + + /** + * @brief Connects the given port to the given net + * If the net is 0 the port is disconnected. + * If non-null, a NetPortRef object will be inserted into the + * net and connected with the given port. + */ + void connect_port (size_t port_id, Net *net); + +private: + friend class Circuit; + friend class Net; + + tl::weak_ptr m_device_class; + std::string m_name; + std::vector m_port_refs; + + /** + * @brief Sets the port reference for a specific port + */ + void set_port_ref_for_port (size_t port_id, Net::port_iterator iter); + + /** + * @brief Sets the device class + */ + void set_device_class (DeviceClass *dc) + { + m_device_class.reset (dc); + } +}; + +/** + * @brief A subcircuit of a circuit + * + * This class essentially is a reference to another circuit + */ +class DB_PUBLIC SubCircuit + : public tl::Object +{ +public: + typedef tl::vector connected_net_list; + + /** + * @brief Default constructor + */ + SubCircuit (); + + /** + * @brief Copy constructor + */ + SubCircuit (const SubCircuit &other); + + /** + * @brief Assignment + */ + SubCircuit &operator= (const SubCircuit &other); + + /** + * @brief Creates a subcircuit reference to the given circuit + */ + SubCircuit (Circuit *circuit, const std::string &name = std::string ()); + + /** + * @brief Destructor + */ + ~SubCircuit (); + + /** + * @brief Gets the circuit the reference points to (const version) + */ + const Circuit *circuit () const + { + return m_circuit.get (); + } + + /** + * @brief Gets the circuit the reference points to (non-const version) + */ + Circuit *circuit () + { + return m_circuit.get (); + } + + /** + * @brief Sets the name of the subcircuit + * + * The name is one way to identify the subcircuit. The transformation is + * another one. + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name of the subcircuit + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the transformation describing the subcircuit + * + * The transformation is a natural description of a subcircuit + * (in contrast to the name) when deriving it from a layout. + */ + void set_trans (const db::DCplxTrans &trans); + + /** + * @brief Gets the transformation describing the subcircuit + */ + const db::DCplxTrans &trans () const + { + return m_trans; + } + + /** + * @brief Gets the net attached to a specific pin + * Returns 0 if no net is attached. + */ + const Net *net_for_pin (size_t pin_id) const; + + /** + * @brief Gets the net attached to a specific pin (non-const version) + * Returns 0 if no net is attached. + */ + Net *net_for_pin (size_t pin_id) + { + return const_cast (((const SubCircuit *) this)->net_for_pin (pin_id)); + } + + /** + * @brief Connects the given pin to the given net + * If the net is 0 the pin is disconnected. + * If non-null, a NetPinRef object will be inserted into the + * net and connected with the given pin. + */ + void connect_pin (size_t pin_id, Net *net); + +private: + friend class Circuit; + friend class Net; + + tl::weak_ptr m_circuit; + std::string m_name; + db::DCplxTrans m_trans; + std::vector m_pin_refs; + + /** + * @brief Sets the pin reference for a specific pin + */ + void set_pin_ref_for_pin (size_t ppin_id, Net::pin_iterator iter); + + /** + * @brief Sets the circuit reference + */ + void set_circuit (Circuit *c) + { + m_circuit.reset (c); + } +}; + /** * @brief A circuit * @@ -566,7 +729,6 @@ public: typedef tl::shared_collection sub_circuit_list; typedef sub_circuit_list::const_iterator const_sub_circuit_iterator; typedef sub_circuit_list::iterator sub_circuit_iterator; - typedef tl::vector connected_net_list; /** * @brief Constructor @@ -838,30 +1000,12 @@ public: } /** - * @brief Gets the net for a given pin of a subcircuit + * @brief Connects the given pin to the given net + * If the net is 0 the pin is disconnected. + * If non-null, a NetPinRef object will be inserted into the + * net and connected with the given pin. */ - const Net *net_for_pin (const SubCircuit *sub_circuit, size_t pin_id) const; - - /** - * @brief Gets the net for a given pin of a subcircuit (non-const version) - */ - Net *net_for_pin (SubCircuit *sub_circuit, size_t pin_id) - { - return const_cast (((const Circuit *) this)->net_for_pin (sub_circuit, pin_id)); - } - - /** - * @brief Gets the net for a given port of a device - */ - const Net *net_for_port (const Device *device, size_t port_id) const; - - /** - * @brief Gets the net for a given port of a device (non-const version) - */ - Net *net_for_port (Device *device, size_t port_id) - { - return const_cast (((const Circuit *) this)->net_for_port (device, port_id)); - } + void connect_pin (size_t pin_id, Net *net); private: friend class Netlist; @@ -874,14 +1018,16 @@ private: device_list m_devices; sub_circuit_list m_sub_circuits; Netlist *mp_netlist; - connected_net_list m_nets_per_pin; - bool m_nets_per_pin_valid; + std::vector m_pin_refs; + + /** + * @brief Sets the pin reference for a specific pin + */ + void set_pin_ref_for_pin (size_t ppin_id, Net::pin_iterator iter); void translate_circuits (const std::map &map); void translate_device_classes (const std::map &map); void set_netlist (Netlist *netlist); - void invalidate_nets_per_pin (); - void validate_nets_per_pin (); }; /** diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index cb95eff06..1a58b856e 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -191,7 +191,7 @@ static std::string netlist2 (const db::Circuit &c) if (! pins.empty ()) { pins += ","; } - const db::Net *net = c.net_for_port (d.operator-> (), i); + const db::Net *net = d->net_for_port (i); pins += d->device_class ()->port_definitions () [i].name (); pins += "="; pins += net ? net->name () : std::string ("(null)"); @@ -210,7 +210,7 @@ static std::string netlist2 (const db::Circuit &c) } pins += s->circuit ()->pin_by_id (i)->name (); pins += "="; - const db::Net *net = c.net_for_pin (s.operator-> (), i); + const db::Net *net = s->net_for_pin (i); pins += net ? net->name () : std::string ("(null)"); } res += " X" + s->name () + ":" + pins + "\n"; @@ -234,6 +234,11 @@ TEST(4_CircuitDevices) std::auto_ptr c (new db::Circuit ()); c->set_name ("c"); + + EXPECT_EQ (netlist2 (*c), + "c:\n" + ); + db::Device *d1 = new db::Device (&dc1, "d1"); db::Device *d2a = new db::Device (&dc2, "d2a"); db::Device *d2b = new db::Device (&dc2, "d2b"); @@ -241,6 +246,13 @@ TEST(4_CircuitDevices) c->add_device (d2a); c->add_device (d2b); + EXPECT_EQ (netlist2 (*c), + "c:\n" + " Dd1:S=(null),G=(null),D=(null)\n" + " Dd2a:A=(null),B=(null)\n" + " Dd2b:A=(null),B=(null)\n" + ); + db::Net *n1 = new db::Net (); n1->set_name ("n1"); EXPECT_EQ (n1->circuit (), 0); @@ -256,6 +268,13 @@ TEST(4_CircuitDevices) n2->add_port (db::NetPortRef (d2a, 1)); n2->add_port (db::NetPortRef (d2b, 0)); + EXPECT_EQ (netlist2 (*c), + "c:\n" + " Dd1:S=n1,G=n2,D=(null)\n" + " Dd2a:A=n1,B=n2\n" + " Dd2b:A=n2,B=(null)\n" + ); + db::Net *n3 = new db::Net (); n3->set_name ("n3"); c->add_net (n3); @@ -349,34 +368,34 @@ TEST(4_NetlistSubcircuits) c1->add_sub_circuit (sc2); db::Net *n2a = new db::Net (); + c2->add_net (n2a); n2a->set_name ("n2a"); n2a->add_pin (db::NetPinRef (0)); n2a->add_port (db::NetPortRef (d, 0)); - c2->add_net (n2a); db::Net *n2b = new db::Net (); + c2->add_net (n2b); n2b->set_name ("n2b"); n2b->add_port (db::NetPortRef (d, 1)); n2b->add_pin (db::NetPinRef (1)); - c2->add_net (n2b); db::Net *n1a = new db::Net (); + c1->add_net (n1a); n1a->set_name ("n1a"); n1a->add_pin (db::NetPinRef (0)); - n1a->add_pin (db::NetPinRef (0, sc1)); - c1->add_net (n1a); + n1a->add_pin (db::NetPinRef (sc1, 0)); db::Net *n1b = new db::Net (); - n1b->set_name ("n1b"); - n1b->add_pin (db::NetPinRef (1, sc1)); - n1b->add_pin (db::NetPinRef (0, sc2)); c1->add_net (n1b); + n1b->set_name ("n1b"); + n1b->add_pin (db::NetPinRef (sc1, 1)); + n1b->add_pin (db::NetPinRef (sc2, 0)); db::Net *n1c = new db::Net (); - n1c->set_name ("n1c"); - n1c->add_pin (db::NetPinRef (1, sc2)); - n1c->add_pin (db::NetPinRef (1)); c1->add_net (n1c); + n1c->set_name ("n1c"); + n1c->add_pin (db::NetPinRef (sc2, 1)); + n1c->add_pin (db::NetPinRef (1)); EXPECT_EQ (nl2string (*nl), "[c1]\n" @@ -396,6 +415,18 @@ TEST(4_NetlistSubcircuits) " DD:A=n2a,B=n2b\n" ); + // check netlist + for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { + for (db::Circuit::net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + for (db::Net::port_iterator i = n->begin_ports (); i != n->end_ports (); ++i) { + EXPECT_EQ (i->net (), n.operator-> ()); + } + for (db::Net::pin_iterator i = n->begin_pins (); i != n->end_pins (); ++i) { + EXPECT_EQ (i->net (), n.operator-> ()); + } + } + } + db::Netlist nl2 = *nl; nl.reset (0); @@ -418,6 +449,18 @@ TEST(4_NetlistSubcircuits) "c2:c2p1=n2a,c2p2=n2b\n" " DD:A=n2a,B=n2b\n" ); + + // check netlist + for (db::Netlist::circuit_iterator c = nl2.begin_circuits (); c != nl2.end_circuits (); ++c) { + for (db::Circuit::net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + for (db::Net::port_iterator i = n->begin_ports (); i != n->end_ports (); ++i) { + EXPECT_EQ (i->net (), n.operator-> ()); + } + for (db::Net::pin_iterator i = n->begin_pins (); i != n->end_pins (); ++i) { + EXPECT_EQ (i->net (), n.operator-> ()); + } + } + } } TEST(5_SubCircuit) @@ -451,3 +494,170 @@ TEST(6_Net) EXPECT_EQ (n.name (), ""); EXPECT_EQ (int (n.cluster_id ()), 0); } + +TEST(7_NetPortsEditing) +{ + db::Circuit c; + db::GenericDeviceClass dc; + dc.add_port_definition (db::DevicePortDefinition ("A", "")); + dc.add_port_definition (db::DevicePortDefinition ("B", "")); + + db::Device *d1 = new db::Device (&dc, "D1"); + c.add_device (d1); + db::Device *d2 = new db::Device (&dc, "D2"); + c.add_device (d2); + + db::Net *n1 = new db::Net (); + n1->set_name ("n1"); + c.add_net (n1); + + db::Net *n2 = new db::Net (); + n2->set_name ("n2"); + c.add_net (n2); + + d1->connect_port (0, n1); + d1->connect_port (1, n2); + + d2->connect_port (1, n1); + d2->connect_port (0, n2); + + EXPECT_EQ (d1->net_for_port (0), n1); + EXPECT_EQ (d1->net_for_port (1), n2); + EXPECT_EQ (d2->net_for_port (0), n2); + EXPECT_EQ (d2->net_for_port (1), n1); + + EXPECT_EQ (net2string (*n1, &c), "D1:A,D2:B"); + EXPECT_EQ (net2string (*n2, &c), "D1:B,D2:A"); + + d1->connect_port (0, n2); + d1->connect_port (1, n1); + + EXPECT_EQ (d1->net_for_port (0), n2); + EXPECT_EQ (d1->net_for_port (1), n1); + + EXPECT_EQ (net2string (*n1, &c), "D2:B,D1:B"); + EXPECT_EQ (net2string (*n2, &c), "D2:A,D1:A"); + + d1->connect_port (0, 0); + EXPECT_EQ (d1->net_for_port (0), 0); + + EXPECT_EQ (net2string (*n1, &c), "D2:B,D1:B"); + EXPECT_EQ (net2string (*n2, &c), "D2:A"); + + delete d1; + d1 = 0; + + EXPECT_EQ (c.begin_devices ()->name (), "D2"); + EXPECT_EQ (++c.begin_devices () == c.end_devices (), true); + + EXPECT_EQ (net2string (*n1, &c), "D2:B"); + EXPECT_EQ (net2string (*n2, &c), "D2:A"); + + delete n1; + n1 = 0; + + EXPECT_EQ (c.begin_nets ()->name (), "n2"); + EXPECT_EQ (++c.begin_nets () == c.end_nets (), true); + + EXPECT_EQ (net2string (*n2, &c), "D2:A"); + + EXPECT_EQ (d2->net_for_port (0), n2); + EXPECT_EQ (d2->net_for_port (1), 0); +} + +TEST(8_NetSubCircuitsEditing) +{ + db::Circuit c; + c.set_name ("c"); + c.add_pin (db::Pin ("X")); + c.add_pin (db::Pin ("Y")); + + db::Circuit cc1; + cc1.set_name ("sc1"); + cc1.add_pin (db::Pin ("A")); + cc1.add_pin (db::Pin ("B")); + + db::Circuit cc2; + cc2.set_name ("sc2"); + cc2.add_pin (db::Pin ("A")); + cc2.add_pin (db::Pin ("B")); + + db::SubCircuit *sc1 = new db::SubCircuit (&cc1, "sc1"); + c.add_sub_circuit (sc1); + + db::SubCircuit *sc2 = new db::SubCircuit (&cc2, "sc2"); + c.add_sub_circuit (sc2); + + db::Net *n1 = new db::Net (); + n1->set_name ("n1"); + c.add_net (n1); + + db::Net *n2 = new db::Net (); + n2->set_name ("n2"); + c.add_net (n2); + + c.connect_pin (0, n1); + EXPECT_EQ (c.net_for_pin (0), n1); + EXPECT_EQ (c.net_for_pin (1), 0); + + sc1->connect_pin (0, n1); + sc1->connect_pin (1, n2); + + sc2->connect_pin (1, n1); + sc2->connect_pin (0, n2); + + EXPECT_EQ (sc1->net_for_pin (0), n1); + EXPECT_EQ (sc1->net_for_pin (1), n2); + EXPECT_EQ (sc2->net_for_pin (0), n2); + EXPECT_EQ (sc2->net_for_pin (1), n1); + + EXPECT_EQ (net2string (*n1, &c), "+X,sc1:A,sc2:B"); + EXPECT_EQ (net2string (*n2, &c), "sc1:B,sc2:A"); + + c.connect_pin (0, 0); + EXPECT_EQ (c.net_for_pin (0), 0); + + EXPECT_EQ (net2string (*n1, &c), "sc1:A,sc2:B"); + EXPECT_EQ (net2string (*n2, &c), "sc1:B,sc2:A"); + + sc1->connect_pin (0, n2); + sc1->connect_pin (1, n1); + + EXPECT_EQ (sc1->net_for_pin (0), n2); + EXPECT_EQ (sc1->net_for_pin (1), n1); + + EXPECT_EQ (net2string (*n1, &c), "sc2:B,sc1:B"); + EXPECT_EQ (net2string (*n2, &c), "sc2:A,sc1:A"); + + sc1->connect_pin (0, 0); + EXPECT_EQ (sc1->net_for_pin (0), 0); + + EXPECT_EQ (net2string (*n1, &c), "sc2:B,sc1:B"); + EXPECT_EQ (net2string (*n2, &c), "sc2:A"); + + delete sc1; + sc1 = 0; + + EXPECT_EQ (c.begin_sub_circuits ()->name (), "sc2"); + EXPECT_EQ (++c.begin_sub_circuits () == c.end_sub_circuits (), true); + + EXPECT_EQ (net2string (*n1, &c), "sc2:B"); + EXPECT_EQ (net2string (*n2, &c), "sc2:A"); + + c.connect_pin (1, n1); + EXPECT_EQ (net2string (*n1, &c), "sc2:B,+Y"); + EXPECT_EQ (c.net_for_pin (1), n1); + + delete n1; + n1 = 0; + + EXPECT_EQ (c.net_for_pin (1), 0); + + EXPECT_EQ (c.begin_nets ()->name (), "n2"); + EXPECT_EQ (++c.begin_nets () == c.end_nets (), true); + + EXPECT_EQ (net2string (*n2, &c), "sc2:A"); + + EXPECT_EQ (sc2->net_for_pin (0), n2); + EXPECT_EQ (sc2->net_for_pin (1), 0); +} From 3c2c72d9ed7fe378fc63019f3495c809382da6b8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Dec 2018 22:31:26 +0100 Subject: [PATCH 098/335] WIP: GSI binding of db::Netlist at al --- src/db/db/db.pro | 3 +- src/db/db/dbNetlist.cc | 3 +- src/db/db/dbNetlist.h | 9 +- src/db/db/gsiDeclDbNetlist.cc | 564 ++++++++++++++++++++++++++++++++++ src/rba/unit_tests/rba.cc | 1 + testdata/ruby/dbNetlist.rb | 71 +++++ 6 files changed, 645 insertions(+), 6 deletions(-) create mode 100644 src/db/db/gsiDeclDbNetlist.cc create mode 100644 testdata/ruby/dbNetlist.rb diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 8b10ea896..b1714201f 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -144,7 +144,8 @@ SOURCES = \ dbHierNetworkProcessor.cc \ dbNetlistProperty.cc \ gsiDeclDbNetlistProperty.cc \ - dbNetlist.cc + dbNetlist.cc \ + gsiDeclDbNetlist.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index e1866f114..bbe1dc381 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -518,10 +518,11 @@ void Circuit::set_cell_index (const db::cell_index_type ci) m_cell_index = ci; } -void Circuit::add_pin (const Pin &pin) +const Pin &Circuit::add_pin (const Pin &pin) { m_pins.push_back (pin); m_pins.back ().set_id (m_pins.size () - 1); + return m_pins.back (); } void Circuit::add_net (Net *net) diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index d31a294f4..b22887454 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -28,6 +28,7 @@ #include "dbTrans.h" #include "tlObjectCollection.h" #include "tlVector.h" +#include "gsiObject.h" #include #include @@ -714,7 +715,7 @@ private: * devices. */ class DB_PUBLIC Circuit - : public tl::Object + : public gsi::ObjectBase, public tl::Object { public: typedef tl::vector pin_list; @@ -803,7 +804,7 @@ public: * * The circuit takes over ownership of the object. */ - void add_pin (const Pin &pin); + const Pin &add_pin(const Pin &pin); /** * @brief Begin iterator for the pins of the circuit (non-const version) @@ -1112,7 +1113,7 @@ private: * A device class describes a type of device. */ class DB_PUBLIC DeviceClass - : public tl::Object + : public gsi::ObjectBase, public tl::Object { public: typedef size_t port_id_type; @@ -1275,7 +1276,7 @@ private: * The circuits represent cells, the device classes type of devices. */ class DB_PUBLIC Netlist - : public tl::Object + : public gsi::ObjectBase, public tl::Object { public: typedef tl::shared_collection circuit_list; diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc new file mode 100644 index 000000000..bb3cbba09 --- /dev/null +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -0,0 +1,564 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "gsiDecl.h" +#include "dbNetlist.h" + +namespace gsi +{ + +Class decl_dbPin ("db", "Pin", + gsi::method ("id", &db::Pin::id, + "@brief Gets the ID of the pin.\n" + ) + + gsi::method ("name", &db::Pin::name, + "@brief Gets the name of the pin.\n" + ), + "@brief A pin of a circuit.\n" + "Pin objects are used to describe the outgoing pins of " + "a circuit. To create a new pin of a circuit, use \\Circuit#create_pin." +); + +static void device_disconnect_port (db::Device *device, size_t port_id) +{ + device->connect_port (port_id, 0); +} + +Class decl_dbDevice ("db", "Device", + gsi::method ("device_class", &db::Device::device_class, + "@brief Gets the device class the device belongs to.\n" + ) + + gsi::method ("name=", &db::Device::set_name, gsi::arg ("name"), + "@brief Sets the name of the device.\n" + "Device names are used to name a device inside a netlist file. " + "Device names should be unique within a circuit." + ) + + gsi::method ("name", &db::Device::name, + "@brief Gets the name of the device.\n" + ) + + gsi::method ("net_for_port", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_port, gsi::arg ("port_id"), + "@brief Gets the net connected to the specified port.\n" + "If the port is not connected, nil is returned for the net." + ) + + gsi::method ("connect_port", &db::Device::connect_port, gsi::arg ("port_id"), gsi::arg ("net"), + "@brief Connects the given port to the specified net.\n" + ) + + gsi::method_ext ("disconnect_port", &device_disconnect_port, gsi::arg ("port_id"), + "@brief Disconnects the given port from any net.\n" + ), + "@brief A device inside a circuit.\n" + "Device object represent atomic devices such as resistors, diodes or transistors. " + "The \\Device class represents a particular device with specific parameters. " + "The type of device is represented by a \\DeviceClass object. Device objects " + "live in \\Circuit objects, the device class objects live in the \\Netlist object.\n" + "\n" + "Devices connect to nets through ports. Ports are described by a port ID which is " + "essentially the zero-based index of the port. Port definitions can be " + "obtained from the device class using the \\DeviceClass#port_definitions method.\n" + "\n" + "Devices connect to nets through the \\Device#connect_port method. " + "Device ports can be disconnected using \\Device#disconnect_port." +); + +static void subcircuit_connect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin, db::Net *net) +{ + if (pin) { + subcircuit->connect_pin (pin->id (), net); + } +} + +static void subcircuit_disconnect_pin (db::SubCircuit *subcircuit, size_t pin_id) +{ + subcircuit->connect_pin (pin_id, 0); +} + +static void subcircuit_disconnect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin) +{ + if (pin) { + subcircuit->connect_pin (pin->id (), 0); + } +} + +Class decl_dbSubCircuit ("db", "SubCircuit", + gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit, + "@brief Gets the circuit referenced by the subcircuit.\n" + ) + + gsi::method ("name=", &db::SubCircuit::set_name, gsi::arg ("name"), + "@brief Sets the name of the subcircuit.\n" + "SubCircuit names are used to name a subcircuits inside a netlist file. " + "SubCircuit names should be unique within a circuit." + ) + + gsi::method ("name", &db::SubCircuit::name, + "@brief Gets the name of the subcircuit.\n" + ) + + gsi::method ("net_for_pin", (db::Net *(db::SubCircuit::*) (size_t)) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"), + "@brief Gets the net connected to the specified pin of the subcircuit.\n" + "If the pin is not connected, nil is returned for the net." + ) + + gsi::method ("connect_pin", &db::SubCircuit::connect_pin, gsi::arg ("pin_id"), gsi::arg ("net"), + "@brief Connects the given pin to the specified net.\n" + ) + + gsi::method_ext ("connect_pin", &gsi::subcircuit_connect_pin1, gsi::arg ("pin"), gsi::arg ("net"), + "@brief Connects the given pin to the specified net.\n" + "This version takes a \\Pin reference instead of a pin ID." + ) + + gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin, gsi::arg ("pin_id"), + "@brief Disconnects the given pin from any net.\n" + ) + + gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin1, gsi::arg ("pin"), + "@brief Disconnects the given pin from any net.\n" + "This version takes a \\Pin reference instead of a pin ID." + ), + "@brief A subcircuit inside a circuit.\n" + "Circuits may instantiate other circuits as subcircuits similar to cells " + "in layouts. Such an instance is a subcircuit. A subcircuit refers to a " + "circuit implementation (a \\Circuit object), and presents connections through " + "pins. The pins of a subcircuit can be connected to nets. The subcircuit pins " + "are identical to the outgoing pins of the circuit the subcircuit refers to.\n" + "\n" + "Subcircuits connect to nets through the \\SubCircuit#connect_pin method. " + "SubCircuit pins can be disconnected using \\SubCircuit#disconnect_pin." +); + +Class decl_dbNetPortRef ("db", "NetPortRef", + gsi::method ("port_id", &db::NetPortRef::port_id, + "@brief Gets the ID of the port of the device the connection is made to." + ) + + gsi::method ("device", (db::Device *(db::NetPortRef::*) ()) &db::NetPortRef::device, + "@brief Gets the device reference.\n" + "Gets the device object that this connection is made to." + ) + + gsi::method ("net", (db::Net *(db::NetPortRef::*) ()) &db::NetPortRef::net, + "@brief Gets the net this port reference is attached to" + ) + + gsi::method ("device_class", (db::DeviceClass *(db::NetPortRef::*) ()) &db::NetPortRef::device_class, + "@brief Gets the class of the device which is addressed." + ) + + gsi::method ("port_def", (db::DevicePortDefinition *(db::NetPortRef::*) ()) &db::NetPortRef::port_def, + "@brief Gets the port definition of the port that is connected" + ), + "@brief A connection to a port of a device.\n" + "This object is used inside a net (see \\Net) to describe the connections a net makes." +); + +Class decl_dbNetPinRef ("db", "NetPinRef", + gsi::method ("pin_id", &db::NetPinRef::pin_id, + "@brief Gets the ID of the pin the connection is made to." + ) + + gsi::method ("pin", &db::NetPinRef::pin, + "@brief Gets the \\Pin object of the pin the connection is made to." + ) + + gsi::method ("subcircuit", (db::SubCircuit *(db::NetPinRef::*) ()) &db::NetPinRef::subcircuit, + "@brief Gets the subcircuit reference.\n" + "If the pin is a pin of a subcircuit, this attribute " + "indicates the subcircuit the net attaches to. The " + "subcircuit lives in the same circuit than the net. " + "If the pin is a outgoing pin of the circuit, this " + "attribute is nil." + ) + + gsi::method ("net", (db::Net *(db::NetPinRef::*) ()) &db::NetPinRef::net, + "@brief Gets the net this pin reference is attached to" + ), + "@brief A connection to a pin of a subcircuit or an outgoing pin of the circuit.\n" + "This object is used inside a net (see \\Net) to describe the connections a net makes." +); + +Class decl_dbNet ("db", "Net", + gsi::method ("circuit", (db::Circuit *(db::Net::*) ()) &db::Net::circuit, + "@brief Gets the circuit the net lives in." + ) + + gsi::method ("clear", &db::Net::clear, + "@brief Clears the net." + ) + + gsi::method ("name=", &db::Net::set_name, gsi::arg ("name"), + "@brief Sets the name of the net.\n" + "The name of the net is used for nameing the net in schematic files for example. " + "The name of the net has to be unique." + ) + + gsi::method ("name", &db::Net::name, + "@brief Gets the name of the net.\n" + "See \\name= for details about the name." + ) + + gsi::method ("cluster_id=", &db::Net::set_cluster_id, gsi::arg ("id"), + "@brief Sets the cluster ID of the net.\n" + "The cluster ID connects the net with a layout cluster. It is set when " + "the net is extracted from a layout." + ) + + gsi::method ("cluster_id", &db::Net::cluster_id, + "@brief Gets the cluster ID of the net.\n" + "See \\cluster_id= for details about the cluster ID." + ) + + gsi::iterator ("each_pin", (db::Net::pin_iterator (db::Net::*) ()) &db::Net::begin_pins, (db::Net::pin_iterator (db::Net::*) ()) &db::Net::end_pins, + "@brief Iterates over all pins the net connects.\n" + "Pin connections are described by \\NetPinRef objects. Pin connections " + "are either connections to subcircuit pins or to outgoing pins of the " + "circuit the net lives in." + ) + + gsi::iterator ("each_port", (db::Net::port_iterator (db::Net::*) ()) &db::Net::begin_ports, (db::Net::port_iterator (db::Net::*) ()) &db::Net::end_ports, + "@brief Iterates over all ports the net connects.\n" + "Ports connect devices. Port connections are described by \\NetPortRef " + "objects." + ), + "@brief A single net.\n" + "A net connects multiple pins or ports together. Pins are either " + "pin or subcircuits of outgoing pins of the circuit the net lives in. " + "Ports are connections made to specific ports of devices.\n" + "\n" + "To connect a net to an outgoing pin of a circuit, use \\Circuit#connect_pin, to " + "disconnect a net from an outgoing pin use \\Circuit#disconnect_pin. " + "To connect a net to a pin of a subcircuit, use \\SubCircuit#connect_pin, to " + "disconnect a net from a pin of a subcircuit, use \\SubCircuit#disconnect_pin. " + "To connect a net to a port of a device, use \\Device#connect_port, to " + "disconnect a net from a port of a device, use \\Device#disconnect_port. " +); + +Class decl_dbDevicePortDefinition ("db", "DevicePortDefinition", + gsi::method ("name", &db::DevicePortDefinition::name, + "@brief Gets the name of the port." + ) + + gsi::method ("name=", &db::DevicePortDefinition::set_name, gsi::arg ("name"), + "@brief Sets the name of the port." + ) + + gsi::method ("description", &db::DevicePortDefinition::description, + "@brief Gets the description of the port." + ) + + gsi::method ("description=", &db::DevicePortDefinition::set_description, gsi::arg ("description"), + "@brief Sets the description of the port." + ) + + gsi::method ("id", &db::DevicePortDefinition::id, + "@brief Gets the ID of the port.\n" + "The ID of the port is used in some places to refer to a specific port (e.g. in " + "the \\NetPortRef object)." + ), + "@brief A port descriptor\n" + "This class is used inside the \\DeviceClass class to describe a port of the device." +); + +Class decl_dbDeviceClass ("db", "DeviceClass", + gsi::method ("name", &db::DeviceClass::name, + "@brief Gets the name of the device class." + ) + + gsi::method ("description", &db::DeviceClass::description, + "@brief Gets the description text of the device class." + ) + + gsi::method ("port_definitions", &db::DeviceClass::port_definitions, + "@brief Gets the list of port definitions of the device.\n" + "See the \\PortDefinition class description for details." + ) + + gsi::method ("port_definition", &db::DeviceClass::port_definition, gsi::arg ("port_id"), + "@brief Gets the port definition object for a given ID.\n" + "Port definition IDs are used in some places to reference a specific port of a device. " + "This method obtains the corresponding definition object." + ), + "@brief A class describing a specific type of device.\n" + "This is the base class for the device class.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_dbGenericDeviceClass (decl_dbDeviceClass, "db", "GenericDeviceClass", + gsi::method ("add_port", &db::GenericDeviceClass::add_port_definition, gsi::arg ("port_def"), + "@brief Adds the given port definition to the device class\n" + "This method will define a new port. The new port is added at the end of existing ports." + ) + + gsi::method ("clear_ports", &db::GenericDeviceClass::clear_port_definitions, + "@brief Clears the list of ports\n" + ) + + gsi::method ("name=", &db::GenericDeviceClass::set_name, gsi::arg ("name"), + "@brief Sets the name of the device\n" + ) + + gsi::method ("description=", &db::GenericDeviceClass::set_description, gsi::arg ("description"), + "@brief Sets the description of the device\n" + ), + "@brief A generic device class\n" + "This class allows building generic device classes. Specificially, ports can be defined " + "by adding port definitions. Port definitions should not be added dynamically. To create " + "your own device, instantiate the \\GenericDeviceClass object, set name and description and " + "specify the ports. Then add this new device class to the \\Netlist object where it will live " + "and be used to define device instances (\\Device objects)." + "\n" + "This class has been added in version 0.26." +); + +static const db::Pin &create_pin (db::Circuit *c, const std::string &name) +{ + return c->add_pin (db::Pin (name)); +} + +static db::Net *create_net (db::Circuit *c) +{ + db::Net *n = new db::Net (); + c->add_net (n); + return n; +} + +static db::Device *create_device (db::Circuit *c) +{ + db::Device *d = new db::Device (); + c->add_device (d); + return d; +} + +static db::Device *create_device1 (db::Circuit *c, db::DeviceClass *dc, const std::string &name) +{ + db::Device *d = new db::Device (dc, name); + c->add_device (d); + return d; +} + +static db::SubCircuit *create_subcircuit (db::Circuit *c) +{ + db::SubCircuit *sc = new db::SubCircuit (); + c->add_sub_circuit (sc); + return sc; +} + +static db::SubCircuit *create_subcircuit1 (db::Circuit *c, db::Circuit *cc, const std::string &name) +{ + db::SubCircuit *sc = new db::SubCircuit (cc, name); + c->add_sub_circuit (sc); + return sc; +} + +static db::Net *circuit_net_for_pin (db::Circuit *c, const db::Pin *pin) +{ + return pin ? c->net_for_pin (pin->id ()) : 0; +} + +static void circuit_connect_pin1 (db::Circuit *c, const db::Pin *pin, db::Net *net) +{ + if (pin) { + c->connect_pin (pin->id (), net); + } +} + +static void circuit_disconnect_pin (db::Circuit *c, size_t pin_id) +{ + c->connect_pin (pin_id, 0); +} + +static void circuit_disconnect_pin1 (db::Circuit *c, const db::Pin *pin) +{ + if (pin) { + c->connect_pin (pin->id (), 0); + } +} + +Class decl_dbCircuit ("db", "Circuit", + gsi::method_ext ("create_pin", &gsi::create_pin, gsi::arg ("name"), + "@brief Creates a new \\Pin object inside the circuit\n" + "This object will describe a pin of the circuit. A circuit connects " + "to the outside through such a pin. The pin is added after all existing " + "pins. For more details see the \\Pin class." + ) + + gsi::iterator ("each_pin", (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::begin_pins, (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::end_pins, + "@brief Iterates over the pins of the circuit" + ) + + gsi::method ("pin_by_id", &db::Circuit::pin_by_id, gsi::arg ("id"), + "@brief Gets the \\Pin object corresponding to a specific ID\n" + "If the ID is not a valid pin ID, nil is returned." + ) + + gsi::method ("pin_count", &db::Circuit::pin_count, + "@brief Gets the number of pins in the circuit" + ) + + gsi::method_ext ("create_net", &gsi::create_net, + "@brief Creates a new \\Net object inside the circuit\n" + "This object will describe a net of the circuit. The nets are basically " + "connections between the different components of the circuit (subcircuits, " + "devices and pins).\n" + "\n" + "A net needs to be filled with references to connect to specific objects. " + "See the \\Net class for more details." + ) + + gsi::method ("remove_net", &db::Circuit::remove_net, gsi::arg ("net"), + "@brief Removes the given net from the circuit\n" + ) + + gsi::iterator ("each_net", (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::begin_nets, (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::end_nets, + "@brief Iterates over the nets of the circuit" + ) + + gsi::method_ext ("create_device", &gsi::create_device, + "@brief Creates a new unbound \\Device object inside the circuit\n" + "This object describes a device of the circuit. Unbound devices need to be attached " + "to a device class before they can be used.\n" + "\n" + "For more details see the \\Device class." + ) + + gsi::method_ext ("create_device", &gsi::create_device1, gsi::arg ("device_class"), gsi::arg ("name", std::string ()), + "@brief Creates a new bound \\Device object inside the circuit\n" + "This object describes a device of the circuit. The device is already attached " + "to the device class. The name is optional and is used to identify the device in a " + "netlist file.\n" + "\n" + "For more details see the \\Device class." + ) + + gsi::method ("remove_device", &db::Circuit::remove_device, gsi::arg ("device"), + "@brief Removes the given device from the circuit\n" + ) + + gsi::iterator ("each_device", (db::Circuit::device_iterator (db::Circuit::*) ()) &db::Circuit::begin_devices, (db::Circuit::device_iterator (db::Circuit::*) ()) &db::Circuit::end_devices, + "@brief Iterates over the devices of the circuit" + ) + + gsi::method_ext ("create_subcircuit", &gsi::create_subcircuit, + "@brief Creates a new unbound \\SubCircuit object inside the circuit\n" + "This object describes an instance of another circuit (sub-circuit). Unbound sub-circuits need " + "to be attached to a circuit before they can be used.\n" + "\n" + "For more details see the \\SubCircuit class." + ) + + gsi::method_ext ("create_subcircuit", &gsi::create_subcircuit1, gsi::arg ("circuit"), gsi::arg ("name", std::string ()), + "@brief Creates a new bound \\SubCircuit object inside the circuit\n" + "This object describes an instance of another circuit inside the circuit. The subcircuit is already attached " + "to the other circuit. The name is optional and is used to identify the subcircuit in a " + "netlist file.\n" + "\n" + "For more details see the \\SubCircuit class." + ) + + gsi::method ("remove_subcircuit", &db::Circuit::remove_sub_circuit, gsi::arg ("subcircuit"), + "@brief Removes the given subcircuit from the circuit\n" + ) + + gsi::iterator ("each_subcircuit", (db::Circuit::sub_circuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_sub_circuits, (db::Circuit::sub_circuit_iterator (db::Circuit::*) ()) &db::Circuit::end_sub_circuits, + "@brief Iterates over the subcircuits of the circuit" + ) + + gsi::method ("netlist", (db::Netlist *(db::Circuit::*) ()) &db::Circuit::netlist, + "@brief Gets the netlist object the circuit lives in" + ) + + gsi::method ("name=", &db::Circuit::set_name, gsi::arg ("name"), + "@brief Sets the name of the circuit" + ) + + gsi::method ("name", &db::Circuit::name, + "@brief Gets the name of the circuit" + ) + + gsi::method ("cell_index=", &db::Circuit::set_cell_index, gsi::arg ("cell_index"), + "@brief Sets the cell index\n" + "The cell index relates a circuit with a cell from a layout. It's intended to " + "hold a cell index number if the netlist was extracted from a layout.\n" + ) + + gsi::method ("cell_index", &db::Circuit::cell_index, + "@brief Gets the cell index of the circuit\n" + "See \\cell_index= for details.\n" + ) + + gsi::method ("net_for_pin", (db::Net *(db::Circuit::*) (size_t)) &db::Circuit::net_for_pin, gsi::arg ("pin_id"), + "@brief Gets the net object attached to a specific pin.\n" + "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" + "This method returns nil if the pin is not connected or the pin ID is invalid." + ) + + gsi::method_ext ("net_for_pin", &gsi::circuit_net_for_pin, gsi::arg ("pin"), + "@brief Gets the net object attached to a specific pin.\n" + "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" + "This method returns nil if the pin is not connected or the pin object is nil." + ) + + gsi::method ("connect_pin", &db::Circuit::connect_pin, gsi::arg ("pin_id"), gsi::arg ("net"), + "@brief Connects the given pin with the given net.\n" + "The net must be one inside the circuit. Any previous connected is resolved before this " + "connection is made. A pin can only be connected to one net at a time." + ) + + gsi::method_ext ("connect_pin", &gsi::circuit_connect_pin1, gsi::arg ("pin"), gsi::arg ("net"), + "@brief Connects the given pin with the given net.\n" + "The net and the pin must be objects from inside the circuit. Any previous connected is resolved before this " + "connection is made. A pin can only be connected to one net at a time." + ) + + gsi::method_ext ("disconnect_pin", &gsi::circuit_disconnect_pin, gsi::arg ("pin_id"), + "@brief Disconnects the given pin from any net.\n" + ) + + gsi::method_ext ("disconnect_pin", &gsi::circuit_disconnect_pin1, gsi::arg ("pin"), + "@brief Disconnects the given pin from any net.\n" + ) + + gsi::method ("clear", &db::Circuit::clear, + "@brief Clears the circuit\n" + "This method removes all objects and clears the other attributes." + ), + "@brief Circuits are the basic building blocks of the netlist\n" + "A circuit has pins by which it can connect to the outside. Pins are " + "created using \\create_pin and are represented by the \\Pin class.\n" + "\n" + "Futhermore, a circuit manages the components of the netlist. " + "Components are devices (class \\Device) and subcircuits (class \\SubCircuit). " + "Devices are basic devices such as resistors or transistors. Subcircuits " + "are other circuits to which nets from this circuit connect. " + "Devices are created using the \\create_device method. Subcircuits are " + "created using the \\create_subcircuit method.\n" + "\n" + "Devices are connected through 'ports', subcircuits are connected through " + "their pins. Ports and pins are described by integer ID's in the context of " + "most methods.\n" + "\n" + "Finally, the circuit consists of the nets. Nets connect ports of devices " + "and pins of subcircuits or the circuit itself. Nets are created using " + "\\create_net and are represented by objects of the \\Net class.\n" + "See there for more about nets.\n" + "\n" + "The Circuit object is only valid if the netlist object is alive. " + "After the netlist object has been destroyed, the Circuit objects " + "must not be accessed anymore. Doing so, may result in a crash.\n" + "\n" + "The Circuit class has been introduced in version 0.26." +); + +static void add_circuit (db::Netlist *nl, db::Circuit *c) +{ + c->keep (); + nl->add_circuit (c); +} + +static void add_device_class (db::Netlist *nl, db::DeviceClass *cl) +{ + cl->keep (); + nl->add_device_class (cl); +} + +Class decl_dbNetlist ("db", "Netlist", + gsi::method_ext ("add", &gsi::add_circuit, + "@brief Adds the circuit to the netlist\n" + "This method will add the given circuit object to the netlist. " + "After the circuit has been added, it will be owned by the netlist." + ) + + gsi::method ("remove", &db::Netlist::remove_circuit, gsi::arg ("circuit"), + "@brief Removes the given circuit object from the netlist\n" + "After the object has been removed, it becomes invalid and cannot be used further." + ) + + gsi::iterator ("each_circuit", (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_circuits, (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_circuits, + "@brief Iterates over the circuits of the netlist" + ) + + gsi::method_ext ("add", &gsi::add_device_class, + "@brief Adds the device class to the netlist\n" + "This method will add the given device class object to the netlist. " + "After the device class has been added, it will be owned by the netlist." + ) + + gsi::method ("remove", &db::Netlist::remove_device_class, gsi::arg ("device_class"), + "@brief Removes the given device class object from the netlist\n" + "After the object has been removed, it becomes invalid and cannot be used further." + ) + + gsi::iterator ("each_device_class", (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::begin_device_classes, (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::end_device_classes, + "@brief Iterates over the device classes of the netlist" + ), + "@brief The netlist top-level class\n" + "A netlist is a hierarchical structure of circuits. At least one circuit is the " + "top-level circuit, other circuits may be referenced as subcircuits.\n" + "Circuits are created with \\create_circuit and are represented by objects of the \\Circuit class.\n" + "\n" + "Beside circuits, the netlist manages device classes. Device classes describe specific " + "types of devices. Device classes are represented by objects of the \\DeviceClass class " + "and are created using \\create_device_class.\n" + "\n" + "The netlist class has been introduced with version 0.26." +); + +} diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index e6d7dcac7..9b7f1310c 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -110,6 +110,7 @@ RUBYTEST (dbLayoutDiff, "dbLayoutDiff.rb") RUBYTEST (dbLayoutQuery, "dbLayoutQuery.rb") RUBYTEST (dbMatrix, "dbMatrix.rb") RUBYTEST (dbNetlistProperty, "dbNetlistProperty.rb") +RUBYTEST (dbNetlist, "dbNetlist.rb") RUBYTEST (dbPathTest, "dbPathTest.rb") RUBYTEST (dbPCells, "dbPCells.rb") RUBYTEST (dbPointTest, "dbPointTest.rb") diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb new file mode 100644 index 000000000..eab0292cc --- /dev/null +++ b/testdata/ruby/dbNetlist.rb @@ -0,0 +1,71 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2018 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBNetlist_TestClass < TestBase + + def test_1_Basic + + nl = RBA::Netlist::new + c = nl.create_circuit + + c.name = "XYZ" + assert_equal(c.name, "XYZ") + + c.cell_index = 42 + assert_equal(c.cell_index, 42) + + cc = nl.create_circuit + cc.name = "ABC" + + names = [] + nl.each_circuit { |i| names << i.name } + assert_equal(names, [ c.name, cc.name ]) + + # NOTE: this will also remove the circuit from the netlist + cc._destroy + + names = [] + nl.each_circuit { |i| names << i.name } + assert_equal(names, [ c.name ]) + + cc = nl.create_circuit + cc.name = "UVW" + + names = [] + nl.each_circuit { |i| names << i.name } + assert_equal(names, [ c.name, cc.name ]) + + # same as _destroy + nl.remove_circuit(c) + + names = [] + nl.each_circuit { |i| names << i.name } + assert_equal(names, [ cc.name ]) + + end + +end + +load("test_epilogue.rb") From aa5e8852158c1134cf9822d7a91dbf4466e1a44c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 24 Dec 2018 00:08:34 +0100 Subject: [PATCH 099/335] Added Ruby tests for GSI binding of db::Netlist classes --- src/db/db/dbNetlist.cc | 14 +- src/db/db/dbNetlist.h | 8 +- src/db/db/gsiDeclDbNetlist.cc | 82 +++--- src/db/unit_tests/dbNetlistTests.cc | 48 ++-- testdata/ruby/dbNetlist.rb | 372 +++++++++++++++++++++++++++- 5 files changed, 443 insertions(+), 81 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index bbe1dc381..92612bf48 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -289,15 +289,16 @@ NetPinRef &NetPinRef::operator= (const NetPinRef &other) return *this; } -const Pin *NetPinRef::pin (const db::Circuit *c) const +const Pin *NetPinRef::pin () const { if (! mp_subcircuit) { - tl_assert (c != 0); - return c->pin_by_id (m_pin_id); - } else { - tl_assert (mp_subcircuit->circuit () != 0); + if (mp_net && mp_net->circuit ()) { + return mp_net->circuit ()->pin_by_id (m_pin_id); + } + } else if (mp_subcircuit->circuit ()) { return mp_subcircuit->circuit ()->pin_by_id (m_pin_id); } + return 0; } // -------------------------------------------------------------------------------- @@ -645,10 +646,11 @@ const std::string &DeviceClass::description () const return no_description; } -void DeviceClass::add_port_definition (const DevicePortDefinition &pd) +const DevicePortDefinition &DeviceClass::add_port_definition (const DevicePortDefinition &pd) { m_port_definitions.push_back (pd); m_port_definitions.back ().set_id (m_port_definitions.size () - 1); + return m_port_definitions.back (); } void DeviceClass::clear_port_definitions () diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index b22887454..432ec77a2 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -188,11 +188,9 @@ public: /** * @brief Gets the pin reference from the pin id - * - * The circuit is the one where the net is defined. It is used to - * resolve outgoing pints. + * If the pin cannot be resolved, null is returned. */ - const Pin *pin (const Circuit *c) const; + const Pin *pin () const; /** * @brief Gets the subcircuit reference @@ -1177,7 +1175,7 @@ public: /** * @brief Adds a port definition */ - void add_port_definition (const DevicePortDefinition &pd); + const DevicePortDefinition &add_port_definition (const DevicePortDefinition &pd); /** * @brief Clears the port definition diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index bb3cbba09..37e77fd57 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -35,7 +35,9 @@ Class decl_dbPin ("db", "Pin", ), "@brief A pin of a circuit.\n" "Pin objects are used to describe the outgoing pins of " - "a circuit. To create a new pin of a circuit, use \\Circuit#create_pin." + "a circuit. To create a new pin of a circuit, use \\Circuit#create_pin.\n" + "\n" + "This class has been added in version 0.26." ); static void device_disconnect_port (db::Device *device, size_t port_id) @@ -76,7 +78,9 @@ Class decl_dbDevice ("db", "Device", "obtained from the device class using the \\DeviceClass#port_definitions method.\n" "\n" "Devices connect to nets through the \\Device#connect_port method. " - "Device ports can be disconnected using \\Device#disconnect_port." + "Device ports can be disconnected using \\Device#disconnect_port.\n" + "\n" + "This class has been added in version 0.26." ); static void subcircuit_connect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin, db::Net *net) @@ -136,7 +140,9 @@ Class decl_dbSubCircuit ("db", "SubCircuit", "are identical to the outgoing pins of the circuit the subcircuit refers to.\n" "\n" "Subcircuits connect to nets through the \\SubCircuit#connect_pin method. " - "SubCircuit pins can be disconnected using \\SubCircuit#disconnect_pin." + "SubCircuit pins can be disconnected using \\SubCircuit#disconnect_pin.\n" + "\n" + "This class has been added in version 0.26." ); Class decl_dbNetPortRef ("db", "NetPortRef", @@ -157,7 +163,9 @@ Class decl_dbNetPortRef ("db", "NetPortRef", "@brief Gets the port definition of the port that is connected" ), "@brief A connection to a port of a device.\n" - "This object is used inside a net (see \\Net) to describe the connections a net makes." + "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" + "\n" + "This class has been added in version 0.26." ); Class decl_dbNetPinRef ("db", "NetPinRef", @@ -179,7 +187,9 @@ Class decl_dbNetPinRef ("db", "NetPinRef", "@brief Gets the net this pin reference is attached to" ), "@brief A connection to a pin of a subcircuit or an outgoing pin of the circuit.\n" - "This object is used inside a net (see \\Net) to describe the connections a net makes." + "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" + "\n" + "This class has been added in version 0.26." ); Class decl_dbNet ("db", "Net", @@ -228,7 +238,9 @@ Class decl_dbNet ("db", "Net", "To connect a net to a pin of a subcircuit, use \\SubCircuit#connect_pin, to " "disconnect a net from a pin of a subcircuit, use \\SubCircuit#disconnect_pin. " "To connect a net to a port of a device, use \\Device#connect_port, to " - "disconnect a net from a port of a device, use \\Device#disconnect_port. " + "disconnect a net from a port of a device, use \\Device#disconnect_port.\n" + "\n" + "This class has been added in version 0.26." ); Class decl_dbDevicePortDefinition ("db", "DevicePortDefinition", @@ -250,7 +262,9 @@ Class decl_dbDevicePortDefinition ("db", "DevicePortDe "the \\NetPortRef object)." ), "@brief A port descriptor\n" - "This class is used inside the \\DeviceClass class to describe a port of the device." + "This class is used inside the \\DeviceClass class to describe a port of the device.\n" + "\n" + "This class has been added in version 0.26." ); Class decl_dbDeviceClass ("db", "DeviceClass", @@ -270,15 +284,29 @@ Class decl_dbDeviceClass ("db", "DeviceClass", "This method obtains the corresponding definition object." ), "@brief A class describing a specific type of device.\n" - "This is the base class for the device class.\n" + "Device class objects live in the context of a \\Netlist object. After a " + "device class is created, it must be added to the netlist using \\Netlist#add. " + "The netlist will own the device class object. When the netlist is destroyed, the " + "device class object will become invalid.\n" + "\n" + "The \\DeviceClass class is the base class for other device classes.\n" "\n" "This class has been added in version 0.26." ); +static void gdc_add_port_definition (db::GenericDeviceClass *cls, db::DevicePortDefinition *port_def) +{ + if (port_def) { + *port_def = cls->add_port_definition (*port_def); + } +} + Class decl_dbGenericDeviceClass (decl_dbDeviceClass, "db", "GenericDeviceClass", - gsi::method ("add_port", &db::GenericDeviceClass::add_port_definition, gsi::arg ("port_def"), + gsi::method_ext ("add_port", &gsi::gdc_add_port_definition, gsi::arg ("port_def"), "@brief Adds the given port definition to the device class\n" - "This method will define a new port. The new port is added at the end of existing ports." + "This method will define a new port. The new port is added at the end of existing ports. " + "The port definition object passed as the argument is modified to contain the " + "new ID of the port." ) + gsi::method ("clear_ports", &db::GenericDeviceClass::clear_port_definitions, "@brief Clears the list of ports\n" @@ -294,7 +322,7 @@ Class decl_dbGenericDeviceClass (decl_dbDeviceClass, "db "by adding port definitions. Port definitions should not be added dynamically. To create " "your own device, instantiate the \\GenericDeviceClass object, set name and description and " "specify the ports. Then add this new device class to the \\Netlist object where it will live " - "and be used to define device instances (\\Device objects)." + "and be used to define device instances (\\Device objects).\n" "\n" "This class has been added in version 0.26." ); @@ -311,13 +339,6 @@ static db::Net *create_net (db::Circuit *c) return n; } -static db::Device *create_device (db::Circuit *c) -{ - db::Device *d = new db::Device (); - c->add_device (d); - return d; -} - static db::Device *create_device1 (db::Circuit *c, db::DeviceClass *dc, const std::string &name) { db::Device *d = new db::Device (dc, name); @@ -325,13 +346,6 @@ static db::Device *create_device1 (db::Circuit *c, db::DeviceClass *dc, const st return d; } -static db::SubCircuit *create_subcircuit (db::Circuit *c) -{ - db::SubCircuit *sc = new db::SubCircuit (); - c->add_sub_circuit (sc); - return sc; -} - static db::SubCircuit *create_subcircuit1 (db::Circuit *c, db::Circuit *cc, const std::string &name) { db::SubCircuit *sc = new db::SubCircuit (cc, name); @@ -395,13 +409,6 @@ Class decl_dbCircuit ("db", "Circuit", gsi::iterator ("each_net", (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::begin_nets, (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::end_nets, "@brief Iterates over the nets of the circuit" ) + - gsi::method_ext ("create_device", &gsi::create_device, - "@brief Creates a new unbound \\Device object inside the circuit\n" - "This object describes a device of the circuit. Unbound devices need to be attached " - "to a device class before they can be used.\n" - "\n" - "For more details see the \\Device class." - ) + gsi::method_ext ("create_device", &gsi::create_device1, gsi::arg ("device_class"), gsi::arg ("name", std::string ()), "@brief Creates a new bound \\Device object inside the circuit\n" "This object describes a device of the circuit. The device is already attached " @@ -416,13 +423,6 @@ Class decl_dbCircuit ("db", "Circuit", gsi::iterator ("each_device", (db::Circuit::device_iterator (db::Circuit::*) ()) &db::Circuit::begin_devices, (db::Circuit::device_iterator (db::Circuit::*) ()) &db::Circuit::end_devices, "@brief Iterates over the devices of the circuit" ) + - gsi::method_ext ("create_subcircuit", &gsi::create_subcircuit, - "@brief Creates a new unbound \\SubCircuit object inside the circuit\n" - "This object describes an instance of another circuit (sub-circuit). Unbound sub-circuits need " - "to be attached to a circuit before they can be used.\n" - "\n" - "For more details see the \\SubCircuit class." - ) + gsi::method_ext ("create_subcircuit", &gsi::create_subcircuit1, gsi::arg ("circuit"), gsi::arg ("name", std::string ()), "@brief Creates a new bound \\SubCircuit object inside the circuit\n" "This object describes an instance of another circuit inside the circuit. The subcircuit is already attached " @@ -506,8 +506,8 @@ Class decl_dbCircuit ("db", "Circuit", "See there for more about nets.\n" "\n" "The Circuit object is only valid if the netlist object is alive. " - "After the netlist object has been destroyed, the Circuit objects " - "must not be accessed anymore. Doing so, may result in a crash.\n" + "Circuits must be added to a netlist using \\Netlist#add to become " + "part of the netlist.\n" "\n" "The Circuit class has been introduced in version 0.26." ); diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 1a58b856e..c6e5e7b43 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -128,7 +128,7 @@ TEST(3_CircuitBasic) EXPECT_EQ (c2.pin_by_id (2), 0); } -static std::string net2string (const db::Net &n, const db::Circuit *c = 0) +static std::string net2string (const db::Net &n) { std::string res; for (db::Net::const_port_iterator i = n.begin_ports (); i != n.end_ports (); ++i) { @@ -149,7 +149,7 @@ static std::string net2string (const db::Net &n, const db::Circuit *c = 0) } else { res += "+"; } - res += i->pin (c) ? i->pin (c)->name () : "(null)"; + res += i->pin () ? i->pin ()->name () : "(null)"; } return res; } @@ -158,7 +158,7 @@ static std::string nets2string (const db::Circuit &c) { std::string res; for (db::Circuit::const_net_iterator n = c.begin_nets (); n != c.end_nets (); ++n) { - res += net2string (*n, &c); + res += net2string (*n); res += "\n"; } return res; @@ -526,8 +526,8 @@ TEST(7_NetPortsEditing) EXPECT_EQ (d2->net_for_port (0), n2); EXPECT_EQ (d2->net_for_port (1), n1); - EXPECT_EQ (net2string (*n1, &c), "D1:A,D2:B"); - EXPECT_EQ (net2string (*n2, &c), "D1:B,D2:A"); + EXPECT_EQ (net2string (*n1), "D1:A,D2:B"); + EXPECT_EQ (net2string (*n2), "D1:B,D2:A"); d1->connect_port (0, n2); d1->connect_port (1, n1); @@ -535,14 +535,14 @@ TEST(7_NetPortsEditing) EXPECT_EQ (d1->net_for_port (0), n2); EXPECT_EQ (d1->net_for_port (1), n1); - EXPECT_EQ (net2string (*n1, &c), "D2:B,D1:B"); - EXPECT_EQ (net2string (*n2, &c), "D2:A,D1:A"); + EXPECT_EQ (net2string (*n1), "D2:B,D1:B"); + EXPECT_EQ (net2string (*n2), "D2:A,D1:A"); d1->connect_port (0, 0); EXPECT_EQ (d1->net_for_port (0), 0); - EXPECT_EQ (net2string (*n1, &c), "D2:B,D1:B"); - EXPECT_EQ (net2string (*n2, &c), "D2:A"); + EXPECT_EQ (net2string (*n1), "D2:B,D1:B"); + EXPECT_EQ (net2string (*n2), "D2:A"); delete d1; d1 = 0; @@ -550,8 +550,8 @@ TEST(7_NetPortsEditing) EXPECT_EQ (c.begin_devices ()->name (), "D2"); EXPECT_EQ (++c.begin_devices () == c.end_devices (), true); - EXPECT_EQ (net2string (*n1, &c), "D2:B"); - EXPECT_EQ (net2string (*n2, &c), "D2:A"); + EXPECT_EQ (net2string (*n1), "D2:B"); + EXPECT_EQ (net2string (*n2), "D2:A"); delete n1; n1 = 0; @@ -559,7 +559,7 @@ TEST(7_NetPortsEditing) EXPECT_EQ (c.begin_nets ()->name (), "n2"); EXPECT_EQ (++c.begin_nets () == c.end_nets (), true); - EXPECT_EQ (net2string (*n2, &c), "D2:A"); + EXPECT_EQ (net2string (*n2), "D2:A"); EXPECT_EQ (d2->net_for_port (0), n2); EXPECT_EQ (d2->net_for_port (1), 0); @@ -611,14 +611,14 @@ TEST(8_NetSubCircuitsEditing) EXPECT_EQ (sc2->net_for_pin (0), n2); EXPECT_EQ (sc2->net_for_pin (1), n1); - EXPECT_EQ (net2string (*n1, &c), "+X,sc1:A,sc2:B"); - EXPECT_EQ (net2string (*n2, &c), "sc1:B,sc2:A"); + EXPECT_EQ (net2string (*n1), "+X,sc1:A,sc2:B"); + EXPECT_EQ (net2string (*n2), "sc1:B,sc2:A"); c.connect_pin (0, 0); EXPECT_EQ (c.net_for_pin (0), 0); - EXPECT_EQ (net2string (*n1, &c), "sc1:A,sc2:B"); - EXPECT_EQ (net2string (*n2, &c), "sc1:B,sc2:A"); + EXPECT_EQ (net2string (*n1), "sc1:A,sc2:B"); + EXPECT_EQ (net2string (*n2), "sc1:B,sc2:A"); sc1->connect_pin (0, n2); sc1->connect_pin (1, n1); @@ -626,14 +626,14 @@ TEST(8_NetSubCircuitsEditing) EXPECT_EQ (sc1->net_for_pin (0), n2); EXPECT_EQ (sc1->net_for_pin (1), n1); - EXPECT_EQ (net2string (*n1, &c), "sc2:B,sc1:B"); - EXPECT_EQ (net2string (*n2, &c), "sc2:A,sc1:A"); + EXPECT_EQ (net2string (*n1), "sc2:B,sc1:B"); + EXPECT_EQ (net2string (*n2), "sc2:A,sc1:A"); sc1->connect_pin (0, 0); EXPECT_EQ (sc1->net_for_pin (0), 0); - EXPECT_EQ (net2string (*n1, &c), "sc2:B,sc1:B"); - EXPECT_EQ (net2string (*n2, &c), "sc2:A"); + EXPECT_EQ (net2string (*n1), "sc2:B,sc1:B"); + EXPECT_EQ (net2string (*n2), "sc2:A"); delete sc1; sc1 = 0; @@ -641,11 +641,11 @@ TEST(8_NetSubCircuitsEditing) EXPECT_EQ (c.begin_sub_circuits ()->name (), "sc2"); EXPECT_EQ (++c.begin_sub_circuits () == c.end_sub_circuits (), true); - EXPECT_EQ (net2string (*n1, &c), "sc2:B"); - EXPECT_EQ (net2string (*n2, &c), "sc2:A"); + EXPECT_EQ (net2string (*n1), "sc2:B"); + EXPECT_EQ (net2string (*n2), "sc2:A"); c.connect_pin (1, n1); - EXPECT_EQ (net2string (*n1, &c), "sc2:B,+Y"); + EXPECT_EQ (net2string (*n1), "sc2:B,+Y"); EXPECT_EQ (c.net_for_pin (1), n1); delete n1; @@ -656,7 +656,7 @@ TEST(8_NetSubCircuitsEditing) EXPECT_EQ (c.begin_nets ()->name (), "n2"); EXPECT_EQ (++c.begin_nets () == c.end_nets (), true); - EXPECT_EQ (net2string (*n2, &c), "sc2:A"); + EXPECT_EQ (net2string (*n2), "sc2:A"); EXPECT_EQ (sc2->net_for_pin (0), n2); EXPECT_EQ (sc2->net_for_pin (1), 0); diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index eab0292cc..5ab05a419 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -25,10 +25,11 @@ load("test_prologue.rb") class DBNetlist_TestClass < TestBase - def test_1_Basic + def test_1_NetlistBasicCircuit nl = RBA::Netlist::new - c = nl.create_circuit + c = RBA::Circuit::new + nl.add(c) c.name = "XYZ" assert_equal(c.name, "XYZ") @@ -36,7 +37,8 @@ class DBNetlist_TestClass < TestBase c.cell_index = 42 assert_equal(c.cell_index, 42) - cc = nl.create_circuit + cc = RBA::Circuit::new + nl.add(cc) cc.name = "ABC" names = [] @@ -50,7 +52,8 @@ class DBNetlist_TestClass < TestBase nl.each_circuit { |i| names << i.name } assert_equal(names, [ c.name ]) - cc = nl.create_circuit + cc = RBA::Circuit::new + nl.add(cc) cc.name = "UVW" names = [] @@ -58,14 +61,373 @@ class DBNetlist_TestClass < TestBase assert_equal(names, [ c.name, cc.name ]) # same as _destroy - nl.remove_circuit(c) + nl.remove(c) names = [] nl.each_circuit { |i| names << i.name } assert_equal(names, [ cc.name ]) + nl._destroy + assert_equal(cc._destroyed?, true) + + end + + def test_2_NetlistBasicDeviceClass + + nl = RBA::Netlist::new + c = RBA::GenericDeviceClass::new + nl.add(c) + + c.name = "XYZ" + assert_equal(c.name, "XYZ") + + cc = RBA::GenericDeviceClass::new + nl.add(cc) + cc.name = "ABC" + + names = [] + nl.each_device_class { |i| names << i.name } + assert_equal(names, [ c.name, cc.name ]) + + # NOTE: this will also remove the circuit from the netlist + cc._destroy + + names = [] + nl.each_device_class { |i| names << i.name } + assert_equal(names, [ c.name ]) + + cc = RBA::GenericDeviceClass::new + nl.add(cc) + cc.name = "UVW" + + names = [] + nl.each_device_class { |i| names << i.name } + assert_equal(names, [ c.name, cc.name ]) + + # same as _destroy + nl.remove(c) + + names = [] + nl.each_device_class { |i| names << i.name } + assert_equal(names, [ cc.name ]) + + nl._destroy + assert_equal(cc._destroyed?, true) + + end + + def test_3_Pin + + c = RBA::Circuit::new + p1 = c.create_pin("A") + p2 = c.create_pin("B") + + assert_equal(p1.id, 0) + assert_equal(p2.id, 1) + + names = [] + c.each_pin { |p| names << p.name } + assert_equal(names, [ "A", "B" ]) + + end + + def test_4_Device + + nl = RBA::Netlist::new + + dc = RBA::GenericDeviceClass::new + nl.add(dc) + dc.name = "DC" + dc.description = "A device class" + + pd = RBA::DevicePortDefinition::new + pd.name = "A" + pd.description = "Port A" + dc.add_port(pd) + + pd = RBA::DevicePortDefinition::new + pd.name = "B" + pd.description = "Port B" + dc.add_port(pd) + + c = RBA::Circuit::new + nl.add(c) + + d1 = c.create_device(dc) + d1.name = "D1" + assert_equal(d1.name, "D1") + + d2 = c.create_device(dc) + d2.name = "D2" + + names = [] + dcs = [] + c.each_device { |d| names << d.name; dcs << d.device_class.name } + assert_equal(names, [ "D1", "D2" ]) + assert_equal(dcs, [ "DC", "DC" ]) + + c.remove_device(d2) + + names = [] + c.each_device { |d| names << d.name } + assert_equal(names, [ "D1" ]) + + d2 = c.create_device(dc, "D2") + + names = [] + dcs = [] + c.each_device { |d| names << d.name; dcs << d.device_class.name } + assert_equal(names, [ "D1", "D2" ]) + assert_equal(dcs, [ "DC", "DC" ]) + + net = c.create_net + net.name = "NET" + + d1.connect_port(1, net) + assert_equal(d1.net_for_port(1).name, "NET") + assert_equal(d1.net_for_port(0).inspect, "nil") + + d2.connect_port(0, net) + + dnames = [] + net.each_port { |p| dnames << p.device.name + ":" + p.port_def.name } + assert_equal(dnames, [ "D1:B", "D2:A" ]) + dnames = [] + net.each_port { |p| dnames << p.device_class.name + ":" + p.port_id.to_s } + assert_equal(dnames, [ "DC:1", "DC:0" ]) + net.each_port { |p| assert_equal(p.net.name, "NET") } + + d1.disconnect_port(1) + assert_equal(d1.net_for_port(1).inspect, "nil") + + dnames = [] + net.each_port { |p| dnames << p.device.name + ":" + p.port_def.name } + assert_equal(dnames, [ "D2:A" ]) + net.each_port { |p| assert_equal(p.net.name, "NET") } + + net.clear + assert_equal(d1.net_for_port(1).inspect, "nil") + assert_equal(d1.net_for_port(0).inspect, "nil") + + end + + def test_5_SubCircuit + + nl = RBA::Netlist::new + + cc = RBA::Circuit::new + cc.name = "CC" + nl.add(cc) + + p1 = cc.create_pin("A") + p2 = cc.create_pin("B") + + assert_equal(p1.id, 0) + assert_equal(p2.id, 1) + + c = RBA::Circuit::new + c.name = "C" + nl.add(c) + + sc1 = c.create_subcircuit(cc) + sc1.name = "SC1" + assert_equal(sc1.name, "SC1") + assert_equal(sc1.circuit.name, "CC") + + sc2 = c.create_subcircuit(cc) + sc2.name = "SC2" + + names = [] + ccn = [] + c.each_subcircuit { |sc| names << sc.name; ccn << sc.circuit.name } + assert_equal(names, [ "SC1", "SC2" ]) + assert_equal(ccn, [ "CC", "CC" ]) + + c.remove_subcircuit(sc2) + + names = [] + c.each_subcircuit { |sc| names << sc.name} + assert_equal(names, [ "SC1" ]) + + sc2 = c.create_subcircuit(cc, "SC2") + + names = [] + ccn = [] + c.each_subcircuit { |sc| names << sc.name; ccn << sc.circuit.name } + assert_equal(names, [ "SC1", "SC2" ]) + assert_equal(ccn, [ "CC", "CC" ]) + + net = c.create_net + net.name = "NET" + + sc1.connect_pin(1, net) + assert_equal(sc1.net_for_pin(1).name, "NET") + assert_equal(sc1.net_for_pin(0).inspect, "nil") + + sc2.connect_pin(0, net) + + cnames = [] + net.each_pin { |p| cnames << p.subcircuit.name + ":" + p.pin.name } + assert_equal(cnames, [ "SC1:B", "SC2:A" ]) + cnames = [] + net.each_pin { |p| cnames << p.subcircuit.name + ":" + p.pin_id.to_s } + assert_equal(cnames, [ "SC1:1", "SC2:0" ]) + net.each_pin { |p| assert_equal(p.net.name, "NET") } + + sc1.disconnect_pin(1) + assert_equal(sc1.net_for_pin(1).inspect, "nil") + + cnames = [] + net.each_pin { |p| cnames << p.subcircuit.name + ":" + p.pin.name } + assert_equal(cnames, [ "SC2:A" ]) + net.each_pin { |p| assert_equal(p.net.name, "NET") } + + net.clear + assert_equal(sc1.net_for_pin(1).inspec, "nil") + assert_equal(sc1.net_for_pin(0).inspect, "nil") + + end + + def test_5_SubCircuit + + nl = RBA::Netlist::new + + c = RBA::Circuit::new + c.name = "C" + nl.add(c) + + net = c.create_net + + assert_equal(net.circuit.name, "C") + + net.name = "NET" + assert_equal(net.name, "NET") + + net.cluster_id = 42 + assert_equal(net.cluster_id, 42) + + end + + def test_6_GenericDeviceClass + + nl = RBA::Netlist::new + + dc = RBA::GenericDeviceClass::new + nl.add(dc) + dc.name = "DC" + assert_equal(dc.name, "DC") + dc.description = "A device class" + assert_equal(dc.description, "A device class") + + pd = RBA::DevicePortDefinition::new + pd.name = "A" + pd.description = "Port A" + dc.add_port(pd) + + assert_equal(pd.id, 0) + assert_equal(pd.name, "A") + assert_equal(pd.description, "Port A") + + pd = RBA::DevicePortDefinition::new + pd.name = "B" + pd.description = "Port B" + dc.add_port(pd) + + assert_equal(pd.id, 1) + assert_equal(pd.name, "B") + assert_equal(pd.description, "Port B") + + names = [] + dc.port_definitions.each { |pd| names << pd.name } + assert_equal(names, [ "A", "B" ]) + + dc.clear_ports + + names = [] + dc.port_definitions.each { |pd| names << pd.name } + assert_equal(names, []) + + end + + def test_7_Circuit + + nl = RBA::Netlist::new + + c = RBA::Circuit::new + nl.add(c) + assert_equal(c.netlist.object_id, nl.object_id) + c.name = "C" + assert_equal(c.name, "C") + c.cell_index = 42 + assert_equal(c.cell_index, 42) + + pina1 = c.create_pin("A1") + pina2 = c.create_pin("A2") + pinb1 = c.create_pin("B1") + pinb2 = c.create_pin("B2") + assert_equal(c.pin_count, 4) + + assert_equal(pina1.id, 0) + assert_equal(pina2.id, 1) + assert_equal(pinb1.id, 2) + assert_equal(pinb2.id, 3) + assert_equal(c.pin_by_id(0).name, "A1") + assert_equal(c.pin_by_id(3).name, "B2") + + names = [] + c.each_pin { |p| names << p.name } + assert_equal(names, [ "A1", "A2", "B1", "B2" ]) + + net1 = c.create_net + net1.name = "NET1" + + net2 = c.create_net + net2.name = "NET2" + + names = [] + c.each_net { |n| names << n.name } + assert_equal(names, [ "NET1", "NET2" ]) + + c.connect_pin(pina1, net1) + c.connect_pin(pinb1.id, net1) + c.connect_pin(pina2, net2) + c.connect_pin(pinb2.id, net2) + + assert_equal(c.net_for_pin(pina1).name, "NET1") + assert_equal(c.net_for_pin(pinb1.id).name, "NET1") + assert_equal(c.net_for_pin(pina2).name, "NET2") + assert_equal(c.net_for_pin(pinb2.id).name, "NET2") + + c.disconnect_pin(pinb1.id) + c.disconnect_pin(pina2) + + assert_equal(c.net_for_pin(pina1).name, "NET1") + assert_equal(c.net_for_pin(pinb1.id).inspect, "nil") + assert_equal(c.net_for_pin(pina2).inspect, "nil") + assert_equal(c.net_for_pin(pinb2.id).name, "NET2") + + c.remove_net(net1) + + assert_equal(c.net_for_pin(pina1).inspect, "nil") + assert_equal(c.net_for_pin(pinb1.id).inspect, "nil") + assert_equal(c.net_for_pin(pina2).inspect, "nil") + assert_equal(c.net_for_pin(pinb2.id).name, "NET2") + + names = [] + c.each_net { |n| names << n.name } + assert_equal(names, [ "NET2" ]) + + c.clear + + names = [] + c.each_net { |n| names << n.name } + assert_equal(names, []) + + assert_equal(c.pin_count, 0) + end end load("test_epilogue.rb") + From c5222c26e3cbb0725d690ad57e9642641f633f5b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 24 Dec 2018 01:31:06 +0100 Subject: [PATCH 100/335] Added DevicePortProperty class with tests and GSI binding --- src/db/db/dbNetlist.h | 38 +++++++ src/db/db/dbNetlistProperty.cc | 74 ++++++++++++- src/db/db/dbNetlistProperty.h | 109 ++++++++++++-------- src/db/db/gsiDeclDbNetlist.cc | 5 +- src/db/db/gsiDeclDbNetlistProperty.cc | 60 ++++++----- src/db/unit_tests/dbNetlistPropertyTests.cc | 28 +++-- src/db/unit_tests/dbNetlistTests.cc | 28 +++++ testdata/ruby/dbNetlist.rb | 14 ++- testdata/ruby/dbNetlistProperty.rb | 36 +++++-- 9 files changed, 291 insertions(+), 101 deletions(-) diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 432ec77a2..d1553bdcf 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -75,6 +75,25 @@ public: */ NetPortRef &operator= (const NetPortRef &other); + /** + * @brief Comparison + */ + bool operator< (const NetPortRef &other) const + { + if (mp_device != other.mp_device) { + return mp_device < other.mp_device; + } + return m_port_id < other.m_port_id; + } + + /** + * @brief Equality + */ + bool operator== (const NetPortRef &other) const + { + return (mp_device == other.mp_device && m_port_id == other.m_port_id); + } + /** * @brief Gets the device reference */ @@ -178,6 +197,25 @@ public: */ NetPinRef &operator= (const NetPinRef &other); + /** + * @brief Comparison + */ + bool operator< (const NetPinRef &other) const + { + if (mp_subcircuit != other.mp_subcircuit) { + return mp_subcircuit < other.mp_subcircuit; + } + return m_pin_id < other.m_pin_id; + } + + /** + * @brief Equality + */ + bool operator== (const NetPinRef &other) const + { + return (mp_subcircuit == other.mp_subcircuit && m_pin_id == other.m_pin_id); + } + /** * @brief Gets the pin reference (const version) */ diff --git a/src/db/db/dbNetlistProperty.cc b/src/db/db/dbNetlistProperty.cc index 12e3213fa..27757bede 100644 --- a/src/db/db/dbNetlistProperty.cc +++ b/src/db/db/dbNetlistProperty.cc @@ -65,9 +65,10 @@ std::string VariantUserClass::to_string (const void *p) con return ((const db::NetlistProperty *) p)->to_string (); } -void VariantUserClass::read (void *p, tl::Extractor &ex) const +void VariantUserClass::read (void * /*p*/, tl::Extractor & /*ex*/) const { - ((db::NetlistProperty *) p)->read (ex); + // .. nothing yet .. + return; } void VariantUserClass::assign (void *self, const void *other) const @@ -123,7 +124,7 @@ const tl::VariantUserClass *NetlistProperty::variant_class } // -------------------------------------------------------------------------------------------- -// NetlistName Implementation +// NetNameProperty Implementation NetNameProperty::NetNameProperty () : NetlistProperty () @@ -188,9 +189,72 @@ std::string NetNameProperty::to_string () const return "name:" + tl::to_word_or_quoted_string (m_name, valid_netname_chars); } -void NetNameProperty::read (tl::Extractor &ex) +// -------------------------------------------------------------------------------------------- +// DevicePortProperty Implementation + +DevicePortProperty::DevicePortProperty () + : NetlistProperty () { - ex.read_word_or_quoted (m_name, valid_netname_chars); + // .. nothing yet .. +} + +DevicePortProperty::DevicePortProperty (const DevicePortProperty &other) + : NetlistProperty (other), m_port_ref (other.m_port_ref) +{ + // .. nothing yet .. +} + +DevicePortProperty::DevicePortProperty (const db::NetPortRef &p) + : NetlistProperty (), m_port_ref (p) +{ + // .. nothing yet .. +} + +DevicePortProperty &DevicePortProperty::operator= (const DevicePortProperty &other) +{ + NetlistProperty::operator= (other); + if (this != &other) { + m_port_ref = other.m_port_ref; + } + return *this; +} + +void DevicePortProperty::set_port_ref (const db::NetPortRef &p) +{ + m_port_ref = p; +} + +bool DevicePortProperty::equals (const NetlistProperty *p) const +{ + const DevicePortProperty *pp = static_cast (p); + return NetlistProperty::equals (p) && m_port_ref == pp->m_port_ref; +} + +bool DevicePortProperty::less (const NetlistProperty *p) const +{ + if (! NetlistProperty::equals (p)) { + return NetlistProperty::less (p); + } else { + const DevicePortProperty *pp = static_cast (p); + return m_port_ref < pp->m_port_ref; + } +} + +void DevicePortProperty::assign (const NetlistProperty *p) +{ + NetlistProperty::assign (p); + const DevicePortProperty *pp = static_cast (p); + m_port_ref = pp->m_port_ref; +} + + +std::string DevicePortProperty::to_string () const +{ + if (m_port_ref.device () && m_port_ref.port_def ()) { + return "port:" + tl::to_word_or_quoted_string (m_port_ref.device ()->name ()) + ":" + tl::to_word_or_quoted_string (m_port_ref.port_def ()->name ()); + } else { + return "port"; + } } } diff --git a/src/db/db/dbNetlistProperty.h b/src/db/db/dbNetlistProperty.h index 76c47bd40..8e8e6bd47 100644 --- a/src/db/db/dbNetlistProperty.h +++ b/src/db/db/dbNetlistProperty.h @@ -149,14 +149,6 @@ public: { return std::string (); } - - /** - * @brief Pulls the object from a string - */ - virtual void read (tl::Extractor &) - { - // .. nothing yet .. - } }; /** @@ -227,59 +219,86 @@ public: */ virtual std::string to_string () const; - /** - * @brief Pulls the object from a string - */ - virtual void read (tl::Extractor &); - private: std::string m_name; }; -#if 0 // @@@ /** - * @brief A reference to an actual port - */ -class DB_PUBLIC DevicePortRef - : public db::NetlistProperty -{ -public: - DevicePortRef (db::NetPortRef *port); - - // ... - -private: - tl::weak_ptr mp_port; -}; - -/** - * @brief An abstrace reference to a port + * @brief A reference to a device port * - * Abstract references are created when turning a string back into a port. - * Abstract references can be turned into actual port references using - * "to_actual_ref". + * This property is used to mark a shape as a device port reference. + * Such a port reference points to a port of a specific device. + * Attaching such a property to a shape allows connecting the + * net to the device later. */ -class DB_PUBLIC DevicePortAbstractRef +class DB_PUBLIC DevicePortProperty : public db::NetlistProperty { public: - DevicePortAbstractRef (const std::string &device_name, const std::string &port_name); - - // ... + /** + * @brief Creates a netlist name property without a specific name + */ + DevicePortProperty (); /** - * @brief Turns an abstract reference into an actual one - * - * The returned object is either 0, if the translation cannot be done or - * and new'd NetPortRef object. It's the responsibility of the caller - * to delete this object when it's no longer used. + * @brief copy constructor */ - NetPortRef *to_actual_ref (const db::Netlist *netlist) const; + DevicePortProperty (const db::DevicePortProperty &other); + + /** + * @brief Creates a netlist name property with the given name + */ + DevicePortProperty (const db::NetPortRef &port_ref); + + /** + * @brief Assignment + */ + DevicePortProperty &operator= (const DevicePortProperty &other); + + /** + * @brief Sets the port reference + */ + void set_port_ref (const db::NetPortRef &port_ref); + + /** + * @brief Gets the port reference + */ + const db::NetPortRef &port_ref () const + { + return m_port_ref; + } + + /** + * @brief Clones the object + */ + virtual DevicePortProperty *clone () const + { + return new DevicePortProperty (*this); + } + + /** + * @brief Compares two objects (equal). Both types are guaranteed to be the same. + */ + virtual bool equals (const NetlistProperty *) const; + + /** + * @brief Compares two objects (less). Both types are guaranteed to be the same. + */ + virtual bool less (const NetlistProperty *) const; + + /** + * @brief Assigned the other object to self. Both types are guaranteed to be identical. + */ + virtual void assign (const NetlistProperty *); + + /** + * @brief Converts to a string + */ + virtual std::string to_string () const; private: - std::string m_device_name, m_port_name; + db::NetPortRef m_port_ref; }; -#endif } diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 37e77fd57..77f66a39c 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -332,10 +332,11 @@ static const db::Pin &create_pin (db::Circuit *c, const std::string &name) return c->add_pin (db::Pin (name)); } -static db::Net *create_net (db::Circuit *c) +static db::Net *create_net (db::Circuit *c, const std::string &name) { db::Net *n = new db::Net (); c->add_net (n); + n->set_name (name); return n; } @@ -394,7 +395,7 @@ Class decl_dbCircuit ("db", "Circuit", gsi::method ("pin_count", &db::Circuit::pin_count, "@brief Gets the number of pins in the circuit" ) + - gsi::method_ext ("create_net", &gsi::create_net, + gsi::method_ext ("create_net", &gsi::create_net, gsi::arg ("name", std::string ()), "@brief Creates a new \\Net object inside the circuit\n" "This object will describe a net of the circuit. The nets are basically " "connections between the different components of the circuit (subcircuits, " diff --git a/src/db/db/gsiDeclDbNetlistProperty.cc b/src/db/db/gsiDeclDbNetlistProperty.cc index 30b4de22f..b4a2fa80c 100644 --- a/src/db/db/gsiDeclDbNetlistProperty.cc +++ b/src/db/db/gsiDeclDbNetlistProperty.cc @@ -35,37 +35,10 @@ static db::NetlistProperty *new_property () return new db::NetlistProperty (); } -static db::NetlistProperty *from_string (const std::string &str) -{ - tl::Extractor ex (str.c_str ()); - if (ex.at_end ()) { - - return new db::NetlistProperty (); - - } else if (ex.test ("name")) { - - ex.test (":"); - std::auto_ptr n (new db::NetNameProperty ()); - n->read (ex); - return n.release (); - - } else { - - return 0; - - } -} - gsi::Class decl_NetlistProperty ("db", "NetlistProperty", gsi::constructor ("new", &new_property, "@brief Creates a plain netlist property" ) + - gsi::constructor ("from_s", &from_string, gsi::arg ("str"), - "@brief Creates a netlist property from a string\n" - "This method can turn the string returned by \\to_string back into a property object.\n" - "@param str The string to read the property from\n" - "@return A fresh property object created from the string\n" - ) + gsi::method ("to_s", &db::NetlistProperty::to_string, "@brief Convert the property to a string.\n" "@return The string representing this property\n" @@ -113,4 +86,37 @@ gsi::Class decl_NetNameProperty (decl_NetlistProperty, "db" "This class was introduced in version 0.26.\n" ); +// --------------------------------------------------------------- +// db::DevicePortProperty binding + +static db::DevicePortProperty *new_devport () +{ + return new db::DevicePortProperty (); +} + +static db::DevicePortProperty *new_devport2 (const db::NetPortRef &n) +{ + return new db::DevicePortProperty (n); +} + +gsi::Class decl_DevicePortProperty (decl_NetlistProperty, "db", "DevicePortProperty", + gsi::constructor ("new", &new_devport, + "@brief Creates a new device port property" + ) + + gsi::constructor ("new", &new_devport2, gsi::arg ("port_ref"), + "@brief Creates a new device port property with the given port reference" + ) + + gsi::method ("port_ref=", &db::DevicePortProperty::set_port_ref, gsi::arg ("p"), + "@brief Sets the port reference\n" + ) + + gsi::method ("port_ref", &db::DevicePortProperty::port_ref, + "@brief Gets the port reference\n" + ), + "@brief A device port reference property.\n" + "\n" + "The netlist property annotates a shape or other object with a reference to a device port." + "\n\n" + "This class was introduced in version 0.26.\n" +); + } diff --git a/src/db/unit_tests/dbNetlistPropertyTests.cc b/src/db/unit_tests/dbNetlistPropertyTests.cc index 7f82e8072..aca2afcc7 100644 --- a/src/db/unit_tests/dbNetlistPropertyTests.cc +++ b/src/db/unit_tests/dbNetlistPropertyTests.cc @@ -28,7 +28,7 @@ #include -TEST(1_Basic) +TEST(1_NameBasic) { db::NetNameProperty name; EXPECT_EQ (name.to_string (), "name:''"); @@ -45,13 +45,28 @@ TEST(1_Basic) n2.set_name ("\"quoted\""); EXPECT_EQ (n2.to_string (), "name:'\"quoted\"'"); - - tl::Extractor ex ("net42"); - n2.read (ex); - EXPECT_EQ (n2.name (), "net42"); } -TEST(2_Variants) +TEST(2_PortRefBasic) +{ + db::GenericDeviceClass dc; + dc.add_port_definition (db::DevicePortDefinition ("A", "Port A")); + dc.add_port_definition (db::DevicePortDefinition ("B", "Port B")); + + db::Device d (&dc, "D"); + + db::DevicePortProperty dp (db::NetPortRef (&d, 1)); + EXPECT_EQ (dp.to_string (), "port:D:B"); + + dp.set_port_ref (db::NetPortRef (&d, 0)); + EXPECT_EQ (dp.to_string (), "port:D:A"); + EXPECT_EQ (dp.port_ref () == db::NetPortRef (&d, 0), true); + + db::DevicePortProperty dp2 = dp; + EXPECT_EQ (dp2.to_string (), "port:D:A"); +} + +TEST(3_Variants) { std::auto_ptr nn (new db::NetNameProperty ()); nn->set_name ("net42"); @@ -67,3 +82,4 @@ TEST(2_Variants) EXPECT_EQ (vv.is_user (), true); EXPECT_EQ (dynamic_cast(vv.to_user ()).name (), "net42"); } + diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index c6e5e7b43..c5da7a144 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -661,3 +661,31 @@ TEST(8_NetSubCircuitsEditing) EXPECT_EQ (sc2->net_for_pin (0), n2); EXPECT_EQ (sc2->net_for_pin (1), 0); } + +TEST(9_NetPortRefBasics) +{ + db::Device d1, d2; + + EXPECT_EQ (db::NetPortRef (&d1, 0) == db::NetPortRef (&d1, 0), true); + EXPECT_EQ (db::NetPortRef (&d1, 0) == db::NetPortRef (&d1, 1), false); + EXPECT_EQ (db::NetPortRef (&d1, 0) == db::NetPortRef (&d2, 0), false); + + EXPECT_EQ (db::NetPortRef (&d1, 0) < db::NetPortRef (&d1, 0), false); + EXPECT_EQ (db::NetPortRef (&d1, 0) < db::NetPortRef (&d1, 1), true); + EXPECT_EQ (db::NetPortRef (&d1, 1) < db::NetPortRef (&d1, 0), false); + EXPECT_NE ((db::NetPortRef (&d1, 0) < db::NetPortRef (&d2, 0)), (db::NetPortRef (&d2, 0) < db::NetPortRef (&d1, 0))); +} + +TEST(10_NetPinRefBasics) +{ + db::SubCircuit d1, d2; + + EXPECT_EQ (db::NetPinRef (&d1, 0) == db::NetPinRef (&d1, 0), true); + EXPECT_EQ (db::NetPinRef (&d1, 0) == db::NetPinRef (&d1, 1), false); + EXPECT_EQ (db::NetPinRef (&d1, 0) == db::NetPinRef (&d2, 0), false); + + EXPECT_EQ (db::NetPinRef (&d1, 0) < db::NetPinRef (&d1, 0), false); + EXPECT_EQ (db::NetPinRef (&d1, 0) < db::NetPinRef (&d1, 1), true); + EXPECT_EQ (db::NetPinRef (&d1, 1) < db::NetPinRef (&d1, 0), false); + EXPECT_NE ((db::NetPinRef (&d1, 0) < db::NetPinRef (&d2, 0)), (db::NetPinRef (&d2, 0) < db::NetPinRef (&d1, 0))); +} diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 5ab05a419..87b07519e 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -180,8 +180,7 @@ class DBNetlist_TestClass < TestBase assert_equal(names, [ "D1", "D2" ]) assert_equal(dcs, [ "DC", "DC" ]) - net = c.create_net - net.name = "NET" + net = c.create_net("NET") d1.connect_port(1, net) assert_equal(d1.net_for_port(1).name, "NET") @@ -257,8 +256,7 @@ class DBNetlist_TestClass < TestBase assert_equal(names, [ "SC1", "SC2" ]) assert_equal(ccn, [ "CC", "CC" ]) - net = c.create_net - net.name = "NET" + net = c.create_net("NET") sc1.connect_pin(1, net) assert_equal(sc1.net_for_pin(1).name, "NET") @@ -283,12 +281,12 @@ class DBNetlist_TestClass < TestBase net.each_pin { |p| assert_equal(p.net.name, "NET") } net.clear - assert_equal(sc1.net_for_pin(1).inspec, "nil") + assert_equal(sc1.net_for_pin(1).inspect, "nil") assert_equal(sc1.net_for_pin(0).inspect, "nil") end - def test_5_SubCircuit + def test_6_SubCircuitBasic nl = RBA::Netlist::new @@ -308,7 +306,7 @@ class DBNetlist_TestClass < TestBase end - def test_6_GenericDeviceClass + def test_7_GenericDeviceClass nl = RBA::Netlist::new @@ -349,7 +347,7 @@ class DBNetlist_TestClass < TestBase end - def test_7_Circuit + def test_8_Circuit nl = RBA::Netlist::new diff --git a/testdata/ruby/dbNetlistProperty.rb b/testdata/ruby/dbNetlistProperty.rb index c7dae4292..49bee637c 100644 --- a/testdata/ruby/dbNetlistProperty.rb +++ b/testdata/ruby/dbNetlistProperty.rb @@ -31,9 +31,6 @@ class DBNetlistProperty_TestClass < TestBase assert_equal(np.is_a?(RBA::NetlistProperty), true) assert_equal(np.to_s, "") - np2 = RBA::NetlistProperty::from_s("") - assert_equal(np2.is_a?(RBA::NetlistProperty), true) - end def test_2_NetName @@ -47,14 +44,37 @@ class DBNetlistProperty_TestClass < TestBase assert_equal(np.to_s, "name:abc") assert_equal(np.name, "abc") - np2 = RBA::NetlistProperty::from_s("name:xyz") - assert_equal(np2.is_a?(RBA::NetlistProperty), true) - assert_equal(np2.is_a?(RBA::NetNameProperty), true) - assert_equal(np2.name, "xyz") + end + + def test_3_DevicePort + + np = RBA::DevicePortProperty::new + assert_equal(np.is_a?(RBA::DevicePortProperty), true) + assert_equal(np.is_a?(RBA::DevicePortProperty), true) + assert_equal(np.to_s, "port") + + dc = RBA::GenericDeviceClass::new + dp = RBA::DevicePortDefinition::new + dp.name = "A" + dc.add_port(dp) + dp.name = "B" + dc.add_port(dp) + + c = RBA::Circuit::new + d = c.create_device(dc, "D") + + n = c.create_net("NET") + d.connect_port(0, n) + + # there is no other way to produce a NetPortRef object yet + n.each_port { |p| np.port_ref = p } + + assert_equal(np.to_s, "port:D:A") + assert_equal(np.port_ref.device.name, "D") end - def test_3_VariantBinding + def test_4_VariantBinding ly = RBA::Layout::new l1 = ly.insert_layer(RBA::LayerInfo::new(1, 0)) From eb6b043c3be2d442b4739f68957aa30d3803edb4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 24 Dec 2018 13:39:19 +0100 Subject: [PATCH 101/335] Parameter values of db::Device/db::DeviceClass --- src/db/db/dbNetlist.cc | 60 +++++++++++- src/db/db/dbNetlist.h | 136 ++++++++++++++++++++++++- src/db/db/gsiDeclDbNetlist.cc | 147 +++++++++++++++++++++++++++- src/db/unit_tests/dbNetlistTests.cc | 40 ++++++++ testdata/ruby/dbNetlist.rb | 82 +++++++++++++++- 5 files changed, 453 insertions(+), 12 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 92612bf48..d983c5391 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -58,7 +58,7 @@ Device::~Device () } Device::Device (DeviceClass *device_class, const std::string &name) - : m_device_class (device_class), m_name (name) + : mp_device_class (device_class), m_name (name) { // .. nothing yet .. } @@ -72,7 +72,7 @@ Device &Device::operator= (const Device &other) { if (this != &other) { m_name = other.m_name; - m_device_class = other.m_device_class; + mp_device_class = other.mp_device_class; } return *this; } @@ -120,6 +120,41 @@ void Device::connect_port (size_t port_id, Net *net) } } +double Device::parameter_value (size_t param_id) const +{ + if (m_parameters.size () > param_id) { + return m_parameters [param_id]; + } else if (mp_device_class) { + const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (param_id); + if (pd) { + return pd->default_value (); + } + } + return 0.0; +} + +void Device::set_parameter_value (size_t param_id, double v) +{ + if (m_parameters.size () <= param_id) { + + // resize the parameter vector with default values + size_t from_size = m_parameters.size (); + m_parameters.resize (param_id + 1, 0.0); + + if (mp_device_class) { + for (size_t n = from_size; n < param_id; ++n) { + const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (n); + if (pd) { + m_parameters [n] = pd->default_value (); + } + } + } + + } + + m_parameters [param_id] = v; +} + // -------------------------------------------------------------------------------- // SubCircuit class implementation @@ -667,6 +702,27 @@ const DevicePortDefinition *DeviceClass::port_definition (size_t id) const } } +const DeviceParameterDefinition &DeviceClass::add_parameter_definition (const DeviceParameterDefinition &pd) +{ + m_parameter_definitions.push_back (pd); + m_parameter_definitions.back ().set_id (m_parameter_definitions.size () - 1); + return m_parameter_definitions.back (); +} + +void DeviceClass::clear_parameter_definitions () +{ + m_parameter_definitions.clear (); +} + +const DeviceParameterDefinition *DeviceClass::parameter_definition (size_t id) const +{ + if (id < m_parameter_definitions.size ()) { + return & m_parameter_definitions [id]; + } else { + return 0; + } +} + // -------------------------------------------------------------------------------- // GenericDeviceClass class implementation diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index d1553bdcf..656ff3d9a 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -553,7 +553,7 @@ public: */ const DeviceClass *device_class () const { - return m_device_class.get (); + return mp_device_class; } /** @@ -592,13 +592,24 @@ public: */ void connect_port (size_t port_id, Net *net); + /** + * @brief Gets the value for the parameter with the given ID + */ + double parameter_value (size_t param_id) const; + + /** + * @brief Sets the value for the parameter with the given ID + */ + void set_parameter_value (size_t param_id, double v); + private: friend class Circuit; friend class Net; - tl::weak_ptr m_device_class; + DeviceClass *mp_device_class; std::string m_name; std::vector m_port_refs; + std::vector m_parameters; /** * @brief Sets the port reference for a specific port @@ -610,7 +621,7 @@ private: */ void set_device_class (DeviceClass *dc) { - m_device_class.reset (dc); + mp_device_class = dc; } }; @@ -1143,6 +1154,99 @@ private: } }; +/** + * @brief A device parameter definition + */ +class DB_PUBLIC DeviceParameterDefinition +{ +public: + /** + * @brief Creates an empty device parameter definition + */ + DeviceParameterDefinition () + : m_name (), m_description (), m_default_value (0.0), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Creates a device parameter definition with the given name and description + */ + DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0) + : m_name (name), m_description (description), m_default_value (default_value), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Gets the parameter name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the parameter name + */ + void set_name (const std::string &n) + { + m_name = n; + } + + /** + * @brief Gets the parameter description + */ + const std::string &description () const + { + return m_description; + } + + /** + * @brief Sets the parameter description + */ + void set_description (const std::string &d) + { + m_description = d; + } + + /** + * @brief Gets the parameter default value + */ + double default_value () const + { + return m_default_value; + } + + /** + * @brief Sets the parameter description + */ + void set_default_value (double d) + { + m_default_value = d; + } + + /** + * @brief Gets the parameter ID + */ + size_t id () const + { + return m_id; + } + +private: + friend class DeviceClass; + + std::string m_name, m_description; + double m_default_value; + size_t m_id; + + void set_id (size_t id) + { + m_id = id; + } +}; + /** * @brief A device class * @@ -1205,7 +1309,7 @@ public: * The number of ports is constant per class. The index of the port * is used as an ID of the port, hence the order must be static. */ - virtual const std::vector &port_definitions () const + const std::vector &port_definitions () const { return m_port_definitions; } @@ -1225,8 +1329,32 @@ public: */ const DevicePortDefinition *port_definition (size_t id) const; + /** + * @brief Gets the parameter definitions + */ + const std::vector ¶meter_definitions () const + { + return m_parameter_definitions; + } + + /** + * @brief Adds a parameter definition + */ + const DeviceParameterDefinition &add_parameter_definition (const DeviceParameterDefinition &pd); + + /** + * @brief Clears the parameter definition + */ + void clear_parameter_definitions (); + + /** + * @brief Gets the parameter definition from the ID + */ + const DeviceParameterDefinition *parameter_definition (size_t id) const; + private: std::vector m_port_definitions; + std::vector m_parameter_definitions; }; /** diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 77f66a39c..09026e21d 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -22,6 +22,8 @@ #include "gsiDecl.h" #include "dbNetlist.h" +#include "tlException.h" +#include "tlInternational.h" namespace gsi { @@ -45,6 +47,40 @@ static void device_disconnect_port (db::Device *device, size_t port_id) device->connect_port (port_id, 0); } +static bool device_has_param_with_name (const db::DeviceClass *device_class, const std::string &name) +{ + const std::vector &pd = device_class->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->name () == name) { + return true; + } + } + return false; +} + +static size_t device_param_id_for_name (const db::DeviceClass *device_class, const std::string &name) +{ + if (device_class) { + const std::vector &pd = device_class->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->name () == name) { + return i->id (); + } + } + } + throw tl::Exception (tl::to_string (tr ("Invalid parameter name")) + ": '" + name + "'"); +} + +static double device_parameter_value (const db::Device *device, const std::string &name) +{ + return device->parameter_value (device_param_id_for_name (device->device_class (), name)); +} + +static void device_set_parameter_value (db::Device *device, const std::string &name, double value) +{ + return device->set_parameter_value (device_param_id_for_name (device->device_class (), name), value); +} + Class decl_dbDevice ("db", "Device", gsi::method ("device_class", &db::Device::device_class, "@brief Gets the device class the device belongs to.\n" @@ -66,6 +102,20 @@ Class decl_dbDevice ("db", "Device", ) + gsi::method_ext ("disconnect_port", &device_disconnect_port, gsi::arg ("port_id"), "@brief Disconnects the given port from any net.\n" + ) + + gsi::method ("parameter", &db::Device::parameter_value, gsi::arg ("param_id"), + "@brief Gets the parameter value for the given parameter ID." + ) + + gsi::method ("set_parameter", &db::Device::set_parameter_value, gsi::arg ("param_id"), gsi::arg ("value"), + "@brief Sets the parameter value for the given parameter ID." + ) + + gsi::method_ext ("parameter", &gsi::device_parameter_value, gsi::arg ("param_name"), + "@brief Gets the parameter value for the given parameter name.\n" + "If the parameter name is not valid, an exception is thrown." + ) + + gsi::method_ext ("set_parameter", &gsi::device_set_parameter_value, gsi::arg ("param_name"), gsi::arg ("value"), + "@brief Sets the parameter value for the given parameter name.\n" + "If the parameter name is not valid, an exception is thrown." ), "@brief A device inside a circuit.\n" "Device object represent atomic devices such as resistors, diodes or transistors. " @@ -243,7 +293,15 @@ Class decl_dbNet ("db", "Net", "This class has been added in version 0.26." ); +static db::DevicePortDefinition *new_port_definition (const std::string &name, const std::string &description) +{ + return new db::DevicePortDefinition (name, description); +} + Class decl_dbDevicePortDefinition ("db", "DevicePortDefinition", + gsi::constructor ("new", &gsi::new_port_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), + "@brief Creates a new port definition." + ) + gsi::method ("name", &db::DevicePortDefinition::name, "@brief Gets the name of the port." ) + @@ -267,6 +325,45 @@ Class decl_dbDevicePortDefinition ("db", "DevicePortDe "This class has been added in version 0.26." ); +static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value) +{ + return new db::DeviceParameterDefinition (name, description, default_value); +} + +Class decl_dbDeviceParameterDefinition ("db", "DeviceParameterDefinition", + gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0), + "@brief Creates a new parameter definition." + ) + + gsi::method ("name", &db::DeviceParameterDefinition::name, + "@brief Gets the name of the parameter." + ) + + gsi::method ("name=", &db::DeviceParameterDefinition::set_name, gsi::arg ("name"), + "@brief Sets the name of the parameter." + ) + + gsi::method ("description", &db::DeviceParameterDefinition::description, + "@brief Gets the description of the parameter." + ) + + gsi::method ("description=", &db::DeviceParameterDefinition::set_description, gsi::arg ("description"), + "@brief Sets the description of the parameter." + ) + + gsi::method ("default_value", &db::DeviceParameterDefinition::default_value, + "@brief Gets the default value of the parameter." + ) + + gsi::method ("default_value=", &db::DeviceParameterDefinition::set_default_value, gsi::arg ("default_value"), + "@brief Sets the default value of the parameter.\n" + "The default value is used to initialize parameters of \\Device objects." + ) + + gsi::method ("id", &db::DeviceParameterDefinition::id, + "@brief Gets the ID of the parameter.\n" + "The ID of the parameter is used in some places to refer to a specific parameter (e.g. in " + "the \\NetParameterRef object)." + ), + "@brief A parameter descriptor\n" + "This class is used inside the \\DeviceClass class to describe a parameter of the device.\n" + "\n" + "This class has been added in version 0.26." +); + Class decl_dbDeviceClass ("db", "DeviceClass", gsi::method ("name", &db::DeviceClass::name, "@brief Gets the name of the device class." @@ -276,12 +373,29 @@ Class decl_dbDeviceClass ("db", "DeviceClass", ) + gsi::method ("port_definitions", &db::DeviceClass::port_definitions, "@brief Gets the list of port definitions of the device.\n" - "See the \\PortDefinition class description for details." + "See the \\DevicePortDefinition class description for details." ) + gsi::method ("port_definition", &db::DeviceClass::port_definition, gsi::arg ("port_id"), "@brief Gets the port definition object for a given ID.\n" "Port definition IDs are used in some places to reference a specific port of a device. " "This method obtains the corresponding definition object." + ) + + gsi::method ("parameter_definitions", &db::DeviceClass::parameter_definitions, + "@brief Gets the list of parameter definitions of the device.\n" + "See the \\DeviceParameterDefinition class description for details." + ) + + gsi::method ("parameter_definition", &db::DeviceClass::parameter_definition, gsi::arg ("parameter_id"), + "@brief Gets the parameter definition object for a given ID.\n" + "Parameter definition IDs are used in some places to reference a specific parameter of a device. " + "This method obtains the corresponding definition object." + ) + + gsi::method_ext ("has_parameter", &gsi::device_has_param_with_name, gsi::arg ("name"), + "@brief Returns true, if the device class has a parameter with the given name.\n" + ) + + gsi::method_ext ("parameter_id", &gsi::device_param_id_for_name, gsi::arg ("name"), + "@brief Returns the parameter ID of the parameter with the given name.\n" + "An exception is thrown if there is no parameter with the given name. Use \\has_parameter to check " + "whether the name is a valid parameter name." ), "@brief A class describing a specific type of device.\n" "Device class objects live in the context of a \\Netlist object. After a " @@ -301,16 +415,38 @@ static void gdc_add_port_definition (db::GenericDeviceClass *cls, db::DevicePort } } +static void gdc_add_parameter_definition (db::GenericDeviceClass *cls, db::DeviceParameterDefinition *parameter_def) +{ + if (parameter_def) { + *parameter_def = cls->add_parameter_definition (*parameter_def); + } +} + Class decl_dbGenericDeviceClass (decl_dbDeviceClass, "db", "GenericDeviceClass", gsi::method_ext ("add_port", &gsi::gdc_add_port_definition, gsi::arg ("port_def"), "@brief Adds the given port definition to the device class\n" "This method will define a new port. The new port is added at the end of existing ports. " "The port definition object passed as the argument is modified to contain the " - "new ID of the port." + "new ID of the port.\n" + "\n" + "The port is copied into the device class. Modifying the port object later " + "does not have the effect of changing the port definition." ) + gsi::method ("clear_ports", &db::GenericDeviceClass::clear_port_definitions, "@brief Clears the list of ports\n" ) + + gsi::method_ext ("add_parameter", &gsi::gdc_add_parameter_definition, gsi::arg ("parameter_def"), + "@brief Adds the given parameter definition to the device class\n" + "This method will define a new parameter. The new parameter is added at the end of existing parameters. " + "The parameter definition object passed as the argument is modified to contain the " + "new ID of the parameter." + "\n" + "The parameter is copied into the device class. Modifying the parameter object later " + "does not have the effect of changing the parameter definition." + ) + + gsi::method ("clear_parameters", &db::GenericDeviceClass::clear_parameter_definitions, + "@brief Clears the list of parameters\n" + ) + gsi::method ("name=", &db::GenericDeviceClass::set_name, gsi::arg ("name"), "@brief Sets the name of the device\n" ) + @@ -324,6 +460,9 @@ Class decl_dbGenericDeviceClass (decl_dbDeviceClass, "db "specify the ports. Then add this new device class to the \\Netlist object where it will live " "and be used to define device instances (\\Device objects).\n" "\n" + "In addition, parameters can be defined which correspond to values stored inside the " + "specific device instance (\\Device object)." + "\n" "This class has been added in version 0.26." ); @@ -545,7 +684,9 @@ Class decl_dbNetlist ("db", "Netlist", ) + gsi::method ("remove", &db::Netlist::remove_device_class, gsi::arg ("device_class"), "@brief Removes the given device class object from the netlist\n" - "After the object has been removed, it becomes invalid and cannot be used further." + "After the object has been removed, it becomes invalid and cannot be used further. " + "Use this method with care as it may corrupt the internal structure of the netlist. " + "Only use this method when device refers to this device class." ) + gsi::iterator ("each_device_class", (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::begin_device_classes, (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::end_device_classes, "@brief Iterates over the device classes of the netlist" diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index c5da7a144..59320d566 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -33,6 +33,11 @@ static std::string pd2string (const db::DevicePortDefinition &pd) return pd.name () + "(" + pd.description () + ") #" + tl::to_string (pd.id ()); } +static std::string pd2string (const db::DeviceParameterDefinition &pd) +{ + return pd.name () + "(" + pd.description () + ")=" + tl::to_string (pd.default_value ()) + " #" + tl::to_string (pd.id ()); +} + TEST(1_DevicePortDefinition) { db::DevicePortDefinition pd; @@ -54,6 +59,22 @@ TEST(1_DevicePortDefinition) dc.add_port_definition (pd2); EXPECT_EQ (pd2string (dc.port_definitions ()[0]), "name(nothing yet) #0"); EXPECT_EQ (pd2string (dc.port_definitions ()[1]), "name2(now it has something) #1"); + + dc.clear_port_definitions (); + EXPECT_EQ (dc.port_definitions ().empty (), true); + + db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0); + dc.add_parameter_definition (ppd); + + db::DeviceParameterDefinition ppd2 ("P2", "Parameter 2"); + dc.add_parameter_definition (ppd2); + + EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=1 #0"); + EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=0 #1"); + + dc.clear_parameter_definitions (); + EXPECT_EQ (dc.parameter_definitions ().empty (), true); + } TEST(2_DeviceClass) @@ -226,11 +247,15 @@ TEST(4_CircuitDevices) dc1.add_port_definition (db::DevicePortDefinition ("S", "Source")); dc1.add_port_definition (db::DevicePortDefinition ("G", "Gate")); dc1.add_port_definition (db::DevicePortDefinition ("D", "Drain")); + dc1.add_parameter_definition (db::DeviceParameterDefinition ("U", "", 1.0)); + dc1.add_parameter_definition (db::DeviceParameterDefinition ("V", "", 2.0)); db::GenericDeviceClass dc2; dc2.set_name ("dc2"); dc2.add_port_definition (db::DevicePortDefinition ("A", "")); dc2.add_port_definition (db::DevicePortDefinition ("B", "")); + dc2.add_parameter_definition (db::DeviceParameterDefinition ("U", "", 2.0)); + dc2.add_parameter_definition (db::DeviceParameterDefinition ("V", "", 1.0)); std::auto_ptr c (new db::Circuit ()); c->set_name ("c"); @@ -246,6 +271,21 @@ TEST(4_CircuitDevices) c->add_device (d2a); c->add_device (d2b); + EXPECT_EQ (d1->parameter_value (0), 1.0); + EXPECT_EQ (d1->parameter_value (1), 2.0); + EXPECT_EQ (d2a->parameter_value (0), 2.0); + EXPECT_EQ (d2a->parameter_value (1), 1.0); + d1->set_parameter_value (1, 1.5); + EXPECT_EQ (d1->parameter_value (0), 1.0); + EXPECT_EQ (d1->parameter_value (1), 1.5); + d1->set_parameter_value (0, 0.5); + EXPECT_EQ (d1->parameter_value (0), 0.5); + EXPECT_EQ (d1->parameter_value (1), 1.5); + + d2a->set_parameter_value (0, -1.0); + EXPECT_EQ (d2a->parameter_value (0), -1.0); + EXPECT_EQ (d2a->parameter_value (1), 1.0); + EXPECT_EQ (netlist2 (*c), "c:\n" " Dd1:S=(null),G=(null),D=(null)\n" diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 87b07519e..e38207b35 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -317,9 +317,7 @@ class DBNetlist_TestClass < TestBase dc.description = "A device class" assert_equal(dc.description, "A device class") - pd = RBA::DevicePortDefinition::new - pd.name = "A" - pd.description = "Port A" + pd = RBA::DevicePortDefinition::new("A", "Port A") dc.add_port(pd) assert_equal(pd.id, 0) @@ -345,6 +343,38 @@ class DBNetlist_TestClass < TestBase dc.port_definitions.each { |pd| names << pd.name } assert_equal(names, []) + pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1", 2.0) + assert_equal(pd.default_value, 2.0) + pd.default_value = 1.0 + assert_equal(pd.default_value, 1.0) + + dc.add_parameter(pd) + + assert_equal(pd.id, 0) + assert_equal(pd.name, "P1") + assert_equal(pd.description, "Parameter 1") + assert_equal(pd.default_value, 1.0) + + pd = RBA::DeviceParameterDefinition::new("", "") + pd.name = "P2" + pd.description = "Parameter 2" + dc.add_parameter(pd) + + assert_equal(pd.id, 1) + assert_equal(pd.name, "P2") + assert_equal(pd.description, "Parameter 2") + assert_equal(pd.default_value, 0.0) + + names = [] + dc.parameter_definitions.each { |pd| names << pd.name } + assert_equal(names, [ "P1", "P2" ]) + + dc.clear_parameters + + names = [] + dc.parameter_definitions.each { |pd| names << pd.name } + assert_equal(names, []) + end def test_8_Circuit @@ -425,6 +455,52 @@ class DBNetlist_TestClass < TestBase end + def test_9_DeviceParameters + + nl = RBA::Netlist::new + + dc = RBA::GenericDeviceClass::new + dc.name = "DC" + nl.add(dc) + + dc.add_parameter(RBA::DeviceParameterDefinition::new("U", "Parameter U", 1.0)) + dc.add_parameter(RBA::DeviceParameterDefinition::new("V", "Parameter V", 2.0)) + + assert_equal(dc.has_parameter("U"), true) + assert_equal(dc.has_parameter("V"), true) + assert_equal(dc.has_parameter("X"), false) + assert_equal(dc.parameter_id("U"), 0) + assert_equal(dc.parameter_id("V"), 1) + error = false + begin + dc.parameter_id("X") # raises an exception + rescue => ex + error = true + end + assert_equal(error, true) + + c = RBA::Circuit::new + c.name = "C" + nl.add(c) + d1 = c.create_device(dc) + + assert_equal(d1.parameter(0), 1.0) + assert_equal(d1.parameter("U"), 1.0) + assert_equal(d1.parameter(1), 2.0) + assert_equal(d1.parameter("V"), 2.0) + + d1.set_parameter(0, 0.5) + assert_equal(d1.parameter(0), 0.5) + assert_equal(d1.parameter(1), 2.0) + d1.set_parameter("U", -0.5) + assert_equal(d1.parameter(0), -0.5) + assert_equal(d1.parameter(1), 2.0) + d1.set_parameter("V", 42) + assert_equal(d1.parameter(0), -0.5) + assert_equal(d1.parameter(1), 42) + + end + end load("test_epilogue.rb") From e3b795e3342e08b2344b90ce549146ecf538736b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 24 Dec 2018 13:52:17 +0100 Subject: [PATCH 102/335] Unique ID of device class objects, netlist reference in device class. --- src/db/db/dbNetlist.cc | 5 +++++ src/db/db/dbNetlist.h | 27 ++++++++++++++++++++++++++- src/db/db/gsiDeclDbNetlist.cc | 14 ++++++++++++++ testdata/ruby/dbNetlist.rb | 3 +++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index d983c5391..e89aab1db 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -652,11 +652,13 @@ void Circuit::connect_pin (size_t pin_id, Net *net) // DeviceClass class implementation DeviceClass::DeviceClass () + : mp_netlist (0) { // .. nothing yet .. } DeviceClass::DeviceClass (const DeviceClass &other) + : mp_netlist (0) { operator= (other); } @@ -801,16 +803,19 @@ void Netlist::add_circuit (Circuit *circuit) void Netlist::remove_circuit (Circuit *circuit) { + circuit->set_netlist (0); m_circuits.erase (circuit); } void Netlist::add_device_class (DeviceClass *device_class) { m_device_classes.push_back (device_class); + device_class->set_netlist (this); } void Netlist::remove_device_class (DeviceClass *device_class) { + device_class->set_netlist (0); m_device_classes.erase (device_class); } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 656ff3d9a..83ebc36e9 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -28,6 +28,7 @@ #include "dbTrans.h" #include "tlObjectCollection.h" #include "tlVector.h" +#include "tlUniqueId.h" #include "gsiObject.h" #include @@ -1253,7 +1254,7 @@ private: * A device class describes a type of device. */ class DB_PUBLIC DeviceClass - : public gsi::ObjectBase, public tl::Object + : public gsi::ObjectBase, public tl::Object, public tl::UniqueId { public: typedef size_t port_id_type; @@ -1287,6 +1288,22 @@ public: return new DeviceClass (*this); } + /** + * @brief Gets the netlist the device class lives in + */ + db::Netlist *netlist () + { + return mp_netlist; + } + + /** + * @brief Gets the netlist the device class lives in (const version) + */ + const db::Netlist *netlist () const + { + return mp_netlist; + } + /** * @brief Gets the name of the device class * @@ -1353,8 +1370,16 @@ public: const DeviceParameterDefinition *parameter_definition (size_t id) const; private: + friend class Netlist; + std::vector m_port_definitions; std::vector m_parameter_definitions; + db::Netlist *mp_netlist; + + void set_netlist (db::Netlist *nl) + { + mp_netlist = nl; + } }; /** diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 09026e21d..d388f1805 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -364,6 +364,11 @@ Class decl_dbDeviceParameterDefinition ("db", "De "This class has been added in version 0.26." ); +static tl::id_type id_of_device_class (const db::DeviceClass *cls) +{ + return tl::id_of (cls); +} + Class decl_dbDeviceClass ("db", "DeviceClass", gsi::method ("name", &db::DeviceClass::name, "@brief Gets the name of the device class." @@ -371,6 +376,15 @@ Class decl_dbDeviceClass ("db", "DeviceClass", gsi::method ("description", &db::DeviceClass::description, "@brief Gets the description text of the device class." ) + + gsi::method ("netlist", (db::Netlist *(db::DeviceClass::*) ()) &db::DeviceClass::netlist, + "@brief Gets the netlist the device class lives in." + ) + + gsi::method_ext ("id", &gsi::id_of_device_class, + "@brief Gets the unique ID of the device class\n" + "The ID is a unique integer that identifies the device class. Use the ID " + "to check for object identity - i.e. to determine whether two devices share the " + "same device class." + ) + gsi::method ("port_definitions", &db::DeviceClass::port_definitions, "@brief Gets the list of port definitions of the device.\n" "See the \\DevicePortDefinition class description for details." diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index e38207b35..a5fdecad4 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -137,6 +137,7 @@ class DBNetlist_TestClass < TestBase dc = RBA::GenericDeviceClass::new nl.add(dc) + assert_equal(dc.netlist.object_id, nl.object_id) dc.name = "DC" dc.description = "A device class" @@ -158,6 +159,8 @@ class DBNetlist_TestClass < TestBase assert_equal(d1.name, "D1") d2 = c.create_device(dc) + assert_equal(d2.device_class.id, dc.id) + assert_equal(d2.device_class.object_id, dc.object_id) # by virtue of Ruby-to-C++ object mapping d2.name = "D2" names = [] From 764667d8e88b34b231228bf32d9b38d0d0a50777 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 24 Dec 2018 16:55:22 +0100 Subject: [PATCH 103/335] WIP: added algorithm for combining devices - needs testing. --- src/db/db/db.pro | 4 +- src/db/db/dbNetlist.cc | 169 +++++++++++++++++++++++++++ src/db/db/dbNetlist.h | 40 +++++++ src/db/db/dbNetlistDeviceClasses.h | 83 +++++++++++++ src/db/db/dbNetlistDeviceExtractor.h | 154 ++++++++++++++++++++++++ 5 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 src/db/db/dbNetlistDeviceClasses.h create mode 100644 src/db/db/dbNetlistDeviceExtractor.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index b1714201f..335ea762c 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -256,7 +256,9 @@ HEADERS = \ dbLocalOperation.h \ dbHierProcessor.h \ dbNetlistProperty.h \ - dbNetlist.h + dbNetlist.h \ + dbNetlistDeviceClasses.h \ + dbNetlistDeviceExtractor.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index e89aab1db..277a3b21d 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -22,6 +22,8 @@ #include "dbNetlist.h" +#include + namespace db { @@ -648,6 +650,173 @@ void Circuit::connect_pin (size_t pin_id, Net *net) } } +void Circuit::purge_nets () +{ + std::vector nets_to_be_purged; + for (net_iterator n = begin_nets (); n != end_nets (); ++n) { + if (n->floating ()) { + nets_to_be_purged.push_back (n.operator-> ()); + } + } + for (std::vector::const_iterator n = nets_to_be_purged.begin (); n != nets_to_be_purged.end (); ++n) { + delete *n; + } +} + +/** + * @brief Sanity check for device to be removed + */ +static void check_device_before_remove (db::Circuit *c, const db::Device *d) +{ + if (d->device_class () != 0) { + throw tl::Exception (tl::to_string (tr ("Internal error: No device class after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name ()); + } + const std::vector &pd = d->device_class ()->port_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (d->net_for_port (p->id ()) != 0) { + throw tl::Exception (tl::to_string (tr ("Internal error: Port still connected after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name () + ", port=" + p->name ()); + } + } +} + +void Circuit::combine_parallel_devices (const db::DeviceClass &cls) +{ + typedef std::vector key_type; + std::map > combination_candidates; + + // identify the candidates for combination - all devices sharing the same nets + // are candidates for combination in parallel mode + for (device_iterator d = begin_devices (); d != end_devices (); ++d) { + + if (tl::id_of (d->device_class ()) != tl::id_of (&cls)) { + continue; + } + + key_type k; + const std::vector &ports = cls.port_definitions (); + for (std::vector::const_iterator p = ports.begin (); p != ports.end (); ++p) { + const db::Net *n = d->net_for_port (p->id ()); + if (n) { + k.push_back (n); + } + } + + std::sort (k.begin (), k.end ()); + k.erase (std::unique (k.begin (), k.end ()), k.end ()); + combination_candidates[k].push_back (d.operator-> ()); + + } + + // actually combine the devices + for (std::map >::iterator cc = combination_candidates.begin (); cc != combination_candidates.end (); ++cc) { + + std::vector &cl = cc->second; + for (size_t i = 0; i != cl.size () - 1; ++i) { + for (size_t j = i + 1; j != cl.size (); ) { + if (cls.combine_devices (cl [i], cl [j])) { + check_device_before_remove (this, cl [j]); // sanity check + delete cl [j]; + cl.erase (cl.begin () + j); + } else { + ++j; + } + } + } + + } +} + +static std::pair attached_two_devices (db::Net &net, const db::DeviceClass &cls) +{ + if (net.begin_pins () != net.end_pins ()) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } + + db::Device *d1 = 0, *d2 = 0; + + Net::port_iterator p = net.begin_ports (); + if (p == net.end_ports () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } else { + d1 = p->device (); + } + + ++p; + if (p == net.end_ports () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } else { + d2 = p->device (); + } + + ++p; + if (p != net.end_ports () || d1 == d2 || !d1 || !d2) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } else { + return std::make_pair (d1, d2); + } +} + +template +static bool same_or_swapped (const std::pair &p1, const std::pair &p2) +{ + return (p1.first == p2.first && p1.second == p2.second) || (p1.first == p2.second && p1.second == p2.first); +} + +void Circuit::combine_serial_devices (const db::DeviceClass &cls) +{ + for (net_iterator n = begin_nets (); n != end_nets (); ++n) { + + std::pair dd = attached_two_devices (*n, cls); + if (! dd.first) { + continue; + } + + // The net is an internal node: the devices attached to this internal node are + // combination candidates if the number of nets emerging from the attached device pair (not counting + // the internal node we just found) does not exceed the number of pins available for the + // new device. + + std::vector other_nets; + + const std::vector &ports = cls.port_definitions (); + for (std::vector::const_iterator p = ports.begin (); p != ports.end (); ++p) { + db::Net *on; + on = dd.first->net_for_port (p->id ()); + if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) { + other_nets.push_back (on); + } + on = dd.second->net_for_port (p->id ()); + if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) { + other_nets.push_back (on); + } + } + + std::sort (other_nets.begin (), other_nets.end ()); + other_nets.erase (std::unique (other_nets.begin (), other_nets.end ()), other_nets.end ()); + + if (other_nets.size () <= cls.port_definitions().size ()) { + + // found a combination candidate + if (cls.combine_devices (dd.first, dd.second)) { + check_device_before_remove (this, dd.second); // sanity check + delete dd.second; + } + + } + + } +} + +void Circuit::combine_devices () +{ + tl_assert (netlist () != 0); + + for (Netlist::device_class_iterator dc = netlist ()->begin_device_classes (); dc != netlist ()->end_device_classes (); ++dc) { + combine_parallel_devices (*dc); + combine_serial_devices (*dc); + } +} + // -------------------------------------------------------------------------------- // DeviceClass class implementation diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 83ebc36e9..c53b003b2 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -452,6 +452,14 @@ public: return m_ports.end (); } + /** + * @brief Returns true, if the net is floating (has no or only a single connection) + */ + bool floating () const + { + return (m_pins.size () + m_ports.size ()) < 2; + } + private: friend class Circuit; @@ -1056,6 +1064,21 @@ public: */ void connect_pin (size_t pin_id, Net *net); + /** + * @brief Purge unused nets + * + * This method will purge all nets which return "floating". + */ + void purge_nets (); + + /** + * @brief Combine devices + * + * This method will combine devices that can be combined according + * to their device classes "combine_devices" method. + */ + void combine_devices (); + private: friend class Netlist; friend class Net; @@ -1077,6 +1100,8 @@ private: void translate_circuits (const std::map &map); void translate_device_classes (const std::map &map); void set_netlist (Netlist *netlist); + void combine_parallel_devices (const db::DeviceClass &cls); + void combine_serial_devices (const db::DeviceClass &cls); }; /** @@ -1319,6 +1344,21 @@ public: */ virtual const std::string &description () const; + /** + * @brief Combines two devices + * + * This method shall test, whether the two devices can be combined. Both devices + * are guaranteed to share the same device class (this). + * If they cannot be combined, this method shall do nothing and return false. + * If they can be combined, this method shall reconnect the nets of the first + * device and entirely disconnect the nets of the second device. + * The second device will be deleted afterwards. + */ + virtual bool combine_devices (db::Device * /*a*/, db::Device * /*b*/) const + { + return false; + } + /** * @brief Gets the port definitions * diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h new file mode 100644 index 000000000..ab6511907 --- /dev/null +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -0,0 +1,83 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbNetlistDeviceClasses +#define _HDR_dbNetlistDeviceClasses + +#include "dbCommon.h" +#include "dbNetlist.h" + +namespace db +{ + +class DB_PUBLIC DeviceClassResistor + : public db::DeviceClass +{ +public: + + + +}; + +class DB_PUBLIC DeviceClassCapacitor + : public db::DeviceClass +{ +public: + + +}; + +class DB_PUBLIC DeviceClassInductivity + : public db::DeviceClass +{ +public: + + +}; + +class DB_PUBLIC DeviceClassDiode + : public db::DeviceClass +{ +public: + + +}; + +class DB_PUBLIC DeviceClassMOSTransistor + : public db::DeviceClass +{ +public: + + +}; + +class DB_PUBLIC DeviceClassBipolarTransistor + : public db::DeviceClass +{ +public: + + +}; + +} + +#endif diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h new file mode 100644 index 000000000..b486b4e23 --- /dev/null +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -0,0 +1,154 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbNetlistDeviceExtractor +#define _HDR_dbNetlistDeviceExtractor + +#include "dbCommon.h" +#include "dbNetlist.h" +#include "dbHierNetworkProcessor.h" +#include "dbLayout.h" + +#include "gsiObject.h" + +namespace db +{ + +class DB_PUBLIC NetlistDeviceExtractor + : public gsi::ObjectBase +{ +public: + /** + * @brief Default constructor + */ + NetlistDeviceExtractor (); + + /** + * @brief Destructor + */ + ~NetlistDeviceExtractor (); + + // TODO: Do we need to declare input layers? + + /** + * @brief Initializes the extractor + * This method will produce the device classes required for the device extraction. + */ + void initialize (db::Netlist *nl); + + /** + * @brief Performs the extraction + * + * The netlist will be filled with circuits (unless not present yet) to represent the + * cells from the layout. + * + * Devices will be generated inside the netlist's circuits as they are extracted + * from the layout. Inside the layout, device port annotation shapes are created with the + * corresponding DevicePortProperty objects attached. The will be used when extracting + * the nets later to associate nets with device ports. + * + * The definition of the input layers is device class specific. + */ + void extract (db::Layout *layout, const std::vector &layers); + + /** + * @brief Checks the input layers + * This method shall raise an error, if the input layer are not properly defined (e.g. + * too few etc.) + */ + virtual void check_input_layers (db::Layout *layout, const std::vector &layers) const; + + /** + * @brief Creates the device classes + * At least one device class needs to be defined. Use "register_device_class" to register + * the device classes you need. The first device class registered has device class index 0, + * the further ones 1, 2, etc. + */ + virtual void create_device_classes (); + + /** + * @brief Gets the connectivity object used to extract the device geometry + */ + virtual db::Connectivity get_connectivity (const std::vector &layers) const; + + /** + * @brief Extracts the devices from the given shape cluster + * + * The shape cluster is a set of geometries belonging together in terms of the + * connectivity defined by "get_connectivity". The cluster might cover multiple devices, + * so the implementation needs to consider this case. The geometries are already merged. + * + * The implementation of this method shall use "create_device" to create new + * devices based on the geometry found. It shall use "define_port" to define + * ports by which the nets extracted in the network extraction step connect + * to the new devices. + */ + virtual void extract_devices (const std::vector &layer_geometry); + +protected: + /** + * @brief Registers a device class + * The device class object will become owned by the netlist and must not be deleted by + * the caller. + */ + void register_device_class (DeviceClass *device_class); + + /** + * @brief Creates a device + * The device object returned can be configured by the caller, e.g. set parameters. + * It will be owned by the netlist and must not be deleted by the caller. + */ + Device *create_device (unsigned int device_class_index = 0); + + /** + * @brief Defines a device port in the layout (a polygon) + */ + void define_port (Device *device, size_t port_id, size_t layer_index, const db::Polygon &polygon); + + /** + * @brief Defines a device port in the layout (a box) + */ + void define_port (Device *device, size_t port_id, size_t layer_index, const db::Box &box); + + /** + * @brief Defines a point-like device port in the layout + */ + void define_port (Device *device, size_t port_id, size_t layer_index, const db::Point &point); + + /** + * @brief Gets the database unit + */ + double dbu () const + { + return mp_layout->dbu (); + } + +private: + db::Layout *mp_layout; + db::cell_index_type m_cell_index; + db::Netlist *mp_netlist; + db::Circuit *mp_circuit; +}; + +} + +#endif From d9b0b2f775e3ad68d607aceb0fa1dfcb55b06d4c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 24 Dec 2018 17:22:59 +0100 Subject: [PATCH 104/335] WIP: Ability to redefine device combination in Ruby (GenericDeviceClass) --- src/db/db/dbNetlist.cc | 37 +----- src/db/db/dbNetlist.h | 138 ++++++-------------- src/db/db/dbNetlistDeviceClasses.h | 1 + src/db/db/gsiDeclDbNetlist.cc | 47 ++++++- src/db/unit_tests/dbNetlistPropertyTests.cc | 2 +- src/db/unit_tests/dbNetlistTests.cc | 12 +- 6 files changed, 93 insertions(+), 144 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 277a3b21d..0c2a134e3 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -836,22 +836,12 @@ DeviceClass &DeviceClass::operator= (const DeviceClass &other) { if (this != &other) { m_port_definitions = other.m_port_definitions; + m_name = other.m_name; + m_description = other.m_description; } return *this; } -const std::string &DeviceClass::name () const -{ - static std::string no_name; - return no_name; -} - -const std::string &DeviceClass::description () const -{ - static std::string no_description; - return no_description; -} - const DevicePortDefinition &DeviceClass::add_port_definition (const DevicePortDefinition &pd) { m_port_definitions.push_back (pd); @@ -894,29 +884,6 @@ const DeviceParameterDefinition *DeviceClass::parameter_definition (size_t id) c } } -// -------------------------------------------------------------------------------- -// GenericDeviceClass class implementation - -GenericDeviceClass::GenericDeviceClass () -{ - // .. nothing yet .. -} - -GenericDeviceClass::GenericDeviceClass (const GenericDeviceClass &other) -{ - operator= (other); -} - -GenericDeviceClass &GenericDeviceClass::operator= (const GenericDeviceClass &other) -{ - if (this != &other) { - DeviceClass::operator= (other); - m_name = other.m_name; - m_description = other.m_description; - } - return *this; -} - // -------------------------------------------------------------------------------- // Netlist class implementation diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index c53b003b2..7ba2ebc00 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -1305,14 +1305,6 @@ public: */ DeviceClass &operator= (const DeviceClass &other); - /** - * @brief Clears the circuit - */ - virtual DeviceClass *clone () const - { - return new DeviceClass (*this); - } - /** * @brief Gets the netlist the device class lives in */ @@ -1334,7 +1326,18 @@ public: * * The name is a formal name which identifies the class. */ - virtual const std::string &name () const; + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the device name + */ + void set_name (const std::string &n) + { + m_name = n; + } /** * @brief Gets the description text for the device class @@ -1342,21 +1345,17 @@ public: * The description text is a human-readable text that * identifies the device class. */ - virtual const std::string &description () const; + const std::string &description () const + { + return m_description; + } /** - * @brief Combines two devices - * - * This method shall test, whether the two devices can be combined. Both devices - * are guaranteed to share the same device class (this). - * If they cannot be combined, this method shall do nothing and return false. - * If they can be combined, this method shall reconnect the nets of the first - * device and entirely disconnect the nets of the second device. - * The second device will be deleted afterwards. + * @brief Sets the description text */ - virtual bool combine_devices (db::Device * /*a*/, db::Device * /*b*/) const + void set_description (const std::string &d) { - return false; + m_description = d; } /** @@ -1409,9 +1408,33 @@ public: */ const DeviceParameterDefinition *parameter_definition (size_t id) const; + /** + * @brief Clears the circuit + */ + virtual DeviceClass *clone () const + { + return new DeviceClass (*this); + } + + /** + * @brief Combines two devices + * + * This method shall test, whether the two devices can be combined. Both devices + * are guaranteed to share the same device class (this). + * If they cannot be combined, this method shall do nothing and return false. + * If they can be combined, this method shall reconnect the nets of the first + * device and entirely disconnect the nets of the second device. + * The second device will be deleted afterwards. + */ + virtual bool combine_devices (db::Device * /*a*/, db::Device * /*b*/) const + { + return false; + } + private: friend class Netlist; + std::string m_name, m_description; std::vector m_port_definitions; std::vector m_parameter_definitions; db::Netlist *mp_netlist; @@ -1422,81 +1445,6 @@ private: } }; -/** - * @brief A generic device class - * - * The generic device class is a push version of the DeviceClassBase - */ -class DB_PUBLIC GenericDeviceClass - : public DeviceClass -{ -public: - /** - * @brief Constructor - * - * Creates an empty circuit. - */ - GenericDeviceClass (); - - /** - * @brief Copy constructor - */ - GenericDeviceClass (const GenericDeviceClass &other); - - /** - * @brief Assignment - */ - GenericDeviceClass &operator= (const GenericDeviceClass &other); - - /** - * @brief Clears the circuit - */ - virtual DeviceClass *clone () const - { - return new GenericDeviceClass (*this); - } - - /** - * @brief Gets the name of the device class - * - * The name is a formal name which identifies the class. - */ - virtual const std::string &name () const - { - return m_name; - } - - /** - * @brief Sets the device name - */ - void set_name (const std::string &n) - { - m_name = n; - } - - /** - * @brief Gets the description text for the device class - * - * The description text is a human-readable text that - * identifies the device class. - */ - virtual const std::string &description () const - { - return m_description; - } - - /** - * @brief Sets the description text - */ - void set_description (const std::string &d) - { - m_description = d; - } - -private: - std::string m_name, m_description; -}; - /** * @brief The netlist class * diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index ab6511907..255154033 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -36,6 +36,7 @@ public: + }; class DB_PUBLIC DeviceClassCapacitor diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index d388f1805..2ceef14cd 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -422,21 +422,54 @@ Class decl_dbDeviceClass ("db", "DeviceClass", "This class has been added in version 0.26." ); -static void gdc_add_port_definition (db::GenericDeviceClass *cls, db::DevicePortDefinition *port_def) +namespace { + +/** + * @brief A DeviceClass implementation that allows reimplementation of the virtual methods + * + * NOTE: cloning of the generic device class is not supported currently. Hence when the + * netlist is copied, the device class attributes will remain, but the functionality is lost. + */ +class GenericDeviceClass + : public db::DeviceClass +{ +public: + GenericDeviceClass () + : db::DeviceClass () + { + // .. nothing yet .. + } + + virtual bool combine_devices (db::Device *a, db::Device *b) const + { + if (cb_combine_devices.can_issue ()) { + return cb_combine_devices.issue (&db::DeviceClass::combine_devices, a, b); + } else { + return db::DeviceClass::combine_devices (a, b); + } + } + +private: + gsi::Callback cb_combine_devices; +}; + +} + +static void gdc_add_port_definition (GenericDeviceClass *cls, db::DevicePortDefinition *port_def) { if (port_def) { *port_def = cls->add_port_definition (*port_def); } } -static void gdc_add_parameter_definition (db::GenericDeviceClass *cls, db::DeviceParameterDefinition *parameter_def) +static void gdc_add_parameter_definition (GenericDeviceClass *cls, db::DeviceParameterDefinition *parameter_def) { if (parameter_def) { *parameter_def = cls->add_parameter_definition (*parameter_def); } } -Class decl_dbGenericDeviceClass (decl_dbDeviceClass, "db", "GenericDeviceClass", +Class decl_GenericDeviceClass (decl_dbDeviceClass, "db", "GenericDeviceClass", gsi::method_ext ("add_port", &gsi::gdc_add_port_definition, gsi::arg ("port_def"), "@brief Adds the given port definition to the device class\n" "This method will define a new port. The new port is added at the end of existing ports. " @@ -446,7 +479,7 @@ Class decl_dbGenericDeviceClass (decl_dbDeviceClass, "db "The port is copied into the device class. Modifying the port object later " "does not have the effect of changing the port definition." ) + - gsi::method ("clear_ports", &db::GenericDeviceClass::clear_port_definitions, + gsi::method ("clear_ports", &GenericDeviceClass::clear_port_definitions, "@brief Clears the list of ports\n" ) + gsi::method_ext ("add_parameter", &gsi::gdc_add_parameter_definition, gsi::arg ("parameter_def"), @@ -458,13 +491,13 @@ Class decl_dbGenericDeviceClass (decl_dbDeviceClass, "db "The parameter is copied into the device class. Modifying the parameter object later " "does not have the effect of changing the parameter definition." ) + - gsi::method ("clear_parameters", &db::GenericDeviceClass::clear_parameter_definitions, + gsi::method ("clear_parameters", &GenericDeviceClass::clear_parameter_definitions, "@brief Clears the list of parameters\n" ) + - gsi::method ("name=", &db::GenericDeviceClass::set_name, gsi::arg ("name"), + gsi::method ("name=", &GenericDeviceClass::set_name, gsi::arg ("name"), "@brief Sets the name of the device\n" ) + - gsi::method ("description=", &db::GenericDeviceClass::set_description, gsi::arg ("description"), + gsi::method ("description=", &GenericDeviceClass::set_description, gsi::arg ("description"), "@brief Sets the description of the device\n" ), "@brief A generic device class\n" diff --git a/src/db/unit_tests/dbNetlistPropertyTests.cc b/src/db/unit_tests/dbNetlistPropertyTests.cc index aca2afcc7..e415664d4 100644 --- a/src/db/unit_tests/dbNetlistPropertyTests.cc +++ b/src/db/unit_tests/dbNetlistPropertyTests.cc @@ -49,7 +49,7 @@ TEST(1_NameBasic) TEST(2_PortRefBasic) { - db::GenericDeviceClass dc; + db::DeviceClass dc; dc.add_port_definition (db::DevicePortDefinition ("A", "Port A")); dc.add_port_definition (db::DevicePortDefinition ("B", "Port B")); diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 59320d566..423c56351 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -87,7 +87,7 @@ TEST(2_DeviceClass) pd2.set_name ("name2"); pd2.set_description ("now it has something"); - db::GenericDeviceClass dc; + db::DeviceClass dc; dc.set_name ("devname"); dc.set_description ("devdesc"); EXPECT_EQ (dc.name (), "devname"); @@ -102,7 +102,7 @@ TEST(2_DeviceClass) EXPECT_EQ (pd2string (*dc.port_definition (dc.port_definitions ()[1].id ())), "name2(now it has something) #1"); EXPECT_EQ (dc.port_definition (3), 0); - db::GenericDeviceClass dc2 = dc; + db::DeviceClass dc2 = dc; EXPECT_EQ (dc2.name (), "devname"); EXPECT_EQ (dc2.description (), "devdesc"); EXPECT_EQ (dc2.port_definitions ().size (), size_t (2)); @@ -242,7 +242,7 @@ static std::string netlist2 (const db::Circuit &c) TEST(4_CircuitDevices) { - db::GenericDeviceClass dc1; + db::DeviceClass dc1; dc1.set_name ("dc1"); dc1.add_port_definition (db::DevicePortDefinition ("S", "Source")); dc1.add_port_definition (db::DevicePortDefinition ("G", "Gate")); @@ -250,7 +250,7 @@ TEST(4_CircuitDevices) dc1.add_parameter_definition (db::DeviceParameterDefinition ("U", "", 1.0)); dc1.add_parameter_definition (db::DeviceParameterDefinition ("V", "", 2.0)); - db::GenericDeviceClass dc2; + db::DeviceClass dc2; dc2.set_name ("dc2"); dc2.add_port_definition (db::DevicePortDefinition ("A", "")); dc2.add_port_definition (db::DevicePortDefinition ("B", "")); @@ -376,7 +376,7 @@ TEST(4_NetlistSubcircuits) { std::auto_ptr nl (new db::Netlist ()); - db::GenericDeviceClass *dc = new db::GenericDeviceClass (); + db::DeviceClass *dc = new db::DeviceClass (); dc->set_name ("dc2"); dc->add_port_definition (db::DevicePortDefinition ("A", "")); dc->add_port_definition (db::DevicePortDefinition ("B", "")); @@ -538,7 +538,7 @@ TEST(6_Net) TEST(7_NetPortsEditing) { db::Circuit c; - db::GenericDeviceClass dc; + db::DeviceClass dc; dc.add_port_definition (db::DevicePortDefinition ("A", "")); dc.add_port_definition (db::DevicePortDefinition ("B", "")); From 792a420e237a4b5f7461ac4813a6bb04b94dc98a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Dec 2018 00:25:07 +0100 Subject: [PATCH 105/335] WIP: purge_nets and combine_devices for db::Netlist, GSI bindings. --- src/db/db/dbNetlist.cc | 14 ++++++++++++++ src/db/db/dbNetlist.h | 15 +++++++++++++++ src/db/db/gsiDeclDbNetlist.cc | 24 ++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 0c2a134e3..39c04268d 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -955,4 +955,18 @@ void Netlist::remove_device_class (DeviceClass *device_class) m_device_classes.erase (device_class); } +void Netlist::purge_nets () +{ + for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + c->purge_nets (); + } +} + +void Netlist::combine_devices () +{ + for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + c->combine_devices (); + } +} + } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 7ba2ebc00..b3d911144 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -1573,6 +1573,21 @@ public: return m_device_classes.end (); } + /** + * @brief Purge unused nets + * + * This method will purge all nets which return "floating". + */ + void purge_nets (); + + /** + * @brief Combine devices + * + * This method will combine devices that can be combined according + * to their device classes "combine_devices" method. + */ + void combine_devices (); + private: circuit_list m_circuits; device_class_list m_device_classes; diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 2ceef14cd..a9abc0c41 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -671,6 +671,18 @@ Class decl_dbCircuit ("db", "Circuit", gsi::method ("clear", &db::Circuit::clear, "@brief Clears the circuit\n" "This method removes all objects and clears the other attributes." + ) + + gsi::method ("combine_devices", &db::Circuit::combine_devices, + "@brief Combines devices where possible\n" + "This method will combine devices that can be combined according " + "to their device classes 'combine_devices' method.\n" + "For example, serial or parallel resistors can be combined into " + "a single resistor.\n" + ) + + gsi::method ("purge_nets", &db::Circuit::purge_nets, + "@brief Purges floating nets.\n" + "Floating nets can be created as effect of reconnections of devices or pins. " + "This method will eliminate all nets that make less than two connections." ), "@brief Circuits are the basic building blocks of the netlist\n" "A circuit has pins by which it can connect to the outside. Pins are " @@ -737,6 +749,18 @@ Class decl_dbNetlist ("db", "Netlist", ) + gsi::iterator ("each_device_class", (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::begin_device_classes, (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::end_device_classes, "@brief Iterates over the device classes of the netlist" + ) + + gsi::method ("combine_devices", &db::Netlist::combine_devices, + "@brief Combines devices where possible\n" + "This method will combine devices that can be combined according " + "to their device classes 'combine_devices' method.\n" + "For example, serial or parallel resistors can be combined into " + "a single resistor.\n" + ) + + gsi::method ("purge_nets", &db::Netlist::purge_nets, + "@brief Purges floating nets.\n" + "Floating nets can be created as effect of reconnections of devices or pins. " + "This method will eliminate all nets that make less than two connections." ), "@brief The netlist top-level class\n" "A netlist is a hierarchical structure of circuits. At least one circuit is the " From 97a1abb73fe580ea4f34ddd67c8cde1c17739d3c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Dec 2018 02:02:18 +0100 Subject: [PATCH 106/335] WIP: implementation of basic device classes, device extractor. --- src/db/db/db.pro | 4 +- src/db/db/dbNetlistDeviceClasses.cc | 220 ++++++++++++++++++++++++++ src/db/db/dbNetlistDeviceClasses.h | 91 +++++++++-- src/db/db/dbNetlistDeviceExtractor.cc | 170 ++++++++++++++++++++ src/db/db/dbNetlistDeviceExtractor.h | 29 ++-- 5 files changed, 489 insertions(+), 25 deletions(-) create mode 100644 src/db/db/dbNetlistDeviceClasses.cc create mode 100644 src/db/db/dbNetlistDeviceExtractor.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 335ea762c..e2bc42515 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -145,7 +145,9 @@ SOURCES = \ dbNetlistProperty.cc \ gsiDeclDbNetlistProperty.cc \ dbNetlist.cc \ - gsiDeclDbNetlist.cc + gsiDeclDbNetlist.cc \ + dbNetlistDeviceClasses.cc \ + dbNetlistDeviceExtractor.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc new file mode 100644 index 000000000..47ccc5702 --- /dev/null +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -0,0 +1,220 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlistDeviceClasses.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------ +// DeviceClassTwoPortDevice implementation + +bool DeviceClassTwoPortDevice::combine_devices (Device *a, Device *b) const +{ + db::Net *na1 = a->net_for_port (0); + db::Net *na2 = a->net_for_port (1); + db::Net *nb1 = b->net_for_port (0); + db::Net *nb2 = b->net_for_port (1); + + bool res = true; + + if ((na1 == nb1 && na2 == nb2) || (na1 == nb2 && na2 == nb1)) { + + parallel (a, b); + + } else if (na2 == nb1 || na2 == nb2) { + + // serial a(B) to b(A or B) + serial (a, b); + a->connect_port (1, (na2 == nb1 ? nb2 : nb1)); + + } else if (na1 == nb1 || na1 == nb2) { + + // serial a(A) to b(A or B) + serial (a, b); + a->connect_port (0, (na1 == nb1 ? nb2 : nb1)); + + } + + if (res) { + b->connect_port (0, 0); + b->connect_port (1, 0); + return true; + } else { + return false; + } +} + + +// ------------------------------------------------------------------------------------ +// DeviceClassResistor implementation + +DeviceClassResistor::DeviceClassResistor () +{ + add_port_definition (db::DevicePortDefinition ("A", "Port A")); + add_port_definition (db::DevicePortDefinition ("B", "Port B")); + + add_parameter_definition (db::DeviceParameterDefinition ("R", "Resistance (Ohm)", 0.0)); +} + +void DeviceClassResistor::parallel (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb)); +} + +void DeviceClassResistor::serial (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb); +} + +// ------------------------------------------------------------------------------------ +// DeviceClassCapacitor implementation + +DeviceClassCapacitor::DeviceClassCapacitor () +{ + add_port_definition (db::DevicePortDefinition ("A", "Port A")); + add_port_definition (db::DevicePortDefinition ("B", "Port B")); + + add_parameter_definition (db::DeviceParameterDefinition ("C", "Capacitance (Farad)", 0.0)); +} + +void DeviceClassCapacitor::serial (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb)); +} + +void DeviceClassCapacitor::parallel (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb); +} + +// ------------------------------------------------------------------------------------ +// DeviceClassInductor implementation + +DeviceClassInductor::DeviceClassInductor () +{ + add_port_definition (db::DevicePortDefinition ("A", "Port A")); + add_port_definition (db::DevicePortDefinition ("B", "Port B")); + + add_parameter_definition (db::DeviceParameterDefinition ("L", "Inductance (Henry)", 0.0)); +} + +void DeviceClassInductor::parallel (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb)); +} + +void DeviceClassInductor::serial (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb); +} + +// ------------------------------------------------------------------------------------ +// DeviceClassInductor implementation + +DeviceClassDiode::DeviceClassDiode () +{ + add_port_definition (db::DevicePortDefinition ("A", "Anode")); + add_port_definition (db::DevicePortDefinition ("C", "Cathode")); + + add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0)); +} + +bool DeviceClassDiode::combine_devices (Device *a, Device *b) const +{ + const db::Net *na1 = a->net_for_port (0); + const db::Net *na2 = a->net_for_port (1); + const db::Net *nb1 = b->net_for_port (0); + const db::Net *nb2 = b->net_for_port (1); + + if ((na1 == nb1 && na2 == nb2) || (na1 == nb2 && na2 == nb1)) { + + a->set_parameter_value (0, a->parameter_value (0) + b->parameter_value (0)); + b->connect_port (0, 0); + b->connect_port (1, 0); + + return true; + + } else { + return false; + } +} + +// ------------------------------------------------------------------------------------ +// DeviceClassInductor implementation + +DeviceClassMOSTransistor::DeviceClassMOSTransistor () +{ + add_port_definition (db::DevicePortDefinition ("S", "Source")); + add_port_definition (db::DevicePortDefinition ("G", "Gate")); + add_port_definition (db::DevicePortDefinition ("D", "Drain")); + + add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0)); +} + +bool DeviceClassMOSTransistor::combine_devices (Device *a, Device *b) const +{ + const db::Net *nas = a->net_for_port (0); + const db::Net *nag = a->net_for_port (1); + const db::Net *nad = a->net_for_port (2); + const db::Net *nbs = b->net_for_port (0); + const db::Net *nbg = b->net_for_port (1); + const db::Net *nbd = b->net_for_port (2); + + // parallel transistors can be combined into one + if (((nas == nbs && nad == nbd) || (nas == nbd && nad == nbs)) && nag == nbg) { + + // for combination the gate length must be identical + if (fabs (a->parameter_value (0) - b->parameter_value (0)) < 1e-6) { + + a->set_parameter_value (1, a->parameter_value (1) + b->parameter_value (1)); + a->set_parameter_value (2, a->parameter_value (2) + b->parameter_value (2)); + a->set_parameter_value (3, a->parameter_value (3) + b->parameter_value (3)); + b->connect_port (0, 0); + b->connect_port (1, 0); + b->connect_port (2, 0); + + return true; + + } + + } + + return false; +} + +} diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index 255154033..b90edd82c 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -29,54 +29,117 @@ namespace db { +/** + * @brief A basic two-port device class + */ +class DB_PUBLIC DeviceClassTwoPortDevice + : public db::DeviceClass +{ +public: + virtual bool combine_devices (Device *a, Device *b) const; + + virtual void parallel (Device *a, Device *b) const = 0; + virtual void serial (Device *a, Device *b) const = 0; +}; + +/** + * @brief A basic resistor device class + * A resistor defines a single parameter, "R", which is the resistance in Ohm. + * It defines two ports, "A" and "B" for the two terminals. + */ class DB_PUBLIC DeviceClassResistor - : public db::DeviceClass + : public db::DeviceClassTwoPortDevice { public: + DeviceClassResistor (); + virtual db::DeviceClass *clone () const + { + return new DeviceClassResistor (*this); + } - - + virtual void parallel (Device *a, Device *b) const; + virtual void serial (Device *a, Device *b) const; }; +/** + * @brief A basic capacitor device class + * A capacitor defines a single parameter, "C", which is the capacitance in Farad. + * It defines two ports, "A" and "B" for the two terminals. + */ class DB_PUBLIC DeviceClassCapacitor - : public db::DeviceClass + : public db::DeviceClassTwoPortDevice { public: + DeviceClassCapacitor (); + virtual db::DeviceClass *clone () const + { + return new DeviceClassCapacitor (*this); + } + virtual void parallel (Device *a, Device *b) const; + virtual void serial (Device *a, Device *b) const; }; -class DB_PUBLIC DeviceClassInductivity - : public db::DeviceClass +/** + * @brief A basic inductor device class + * An inductor defines a single parameter, "L", which is the inductance in Henry. + * It defines two ports, "A" and "B" for the two terminals. + */ +class DB_PUBLIC DeviceClassInductor + : public db::DeviceClassTwoPortDevice { public: + DeviceClassInductor (); + virtual db::DeviceClass *clone () const + { + return new DeviceClassInductor (*this); + } + virtual void parallel (Device *a, Device *b) const; + virtual void serial (Device *a, Device *b) const; }; +/** + * @brief A basic diode device class + * A diode defines a single parameter, "A", which is the area in square micrometers (YES: micrometers, as this is the basic unit of measure + * in KLayout). + * It defines two ports, "A" and "C" for anode and cathode. + */ class DB_PUBLIC DeviceClassDiode : public db::DeviceClass { public: + DeviceClassDiode (); + virtual db::DeviceClass *clone () const + { + return new DeviceClassDiode (*this); + } + virtual bool combine_devices (Device *a, Device *b) const; }; +/** + * @brief A basic MOSFET device class + * A MOSFET defines four parameters: "W" for the gate width in micrometers, "L" for the gate length in micrometers, + * "AS" for the source area and "AD" for the drain area. + * The MOSFET device defines three ports, "S", "D" and "G" for source, drain and gate. + */ class DB_PUBLIC DeviceClassMOSTransistor : public db::DeviceClass { public: + DeviceClassMOSTransistor (); + virtual db::DeviceClass *clone () const + { + return new DeviceClassMOSTransistor (*this); + } -}; - -class DB_PUBLIC DeviceClassBipolarTransistor - : public db::DeviceClass -{ -public: - - + virtual bool combine_devices (Device *a, Device *b) const; }; } diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc new file mode 100644 index 000000000..8e5b30908 --- /dev/null +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -0,0 +1,170 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlistDeviceExtractor.h" +#include "dbHierNetworkProcessor.h" +#include "dbNetlistProperty.h" +#include "dbRegion.h" + +namespace db +{ + +NetlistDeviceExtractor::NetlistDeviceExtractor () + : mp_layout (0), m_cell_index (0), mp_circuit (0) +{ + m_device_name_index = 0; + m_propname_id = 0; +} + +NetlistDeviceExtractor::~NetlistDeviceExtractor () +{ + // .. nothing yet .. +} + +void NetlistDeviceExtractor::initialize (db::Netlist *nl) +{ + m_device_classes.clear (); + m_device_name_index = 0; + m_propname_id = 0; + m_netlist.reset (nl); + create_device_classes (); +} + +static void insert_into_region (const db::PolygonRef &s, const db::ICplxTrans &tr, db::Region ®ion) +{ + region.insert (s.obj ().transformed (tr * db::ICplxTrans (s.trans ()))); +} + +void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers) +{ + typedef db::PolygonRef shape_type; + db::ShapeIterator::flags_type shape_iter_flags = db::ShapeIterator::Polygons; + + mp_layout = &layout; + // port properties are kept in property index 0 + m_propname_id = mp_layout->properties_repository ().prop_name_id (tl::Variant (int (0))); + + tl_assert (m_netlist.get () != 0); + + std::set called_cells; + called_cells.insert (cell.cell_index ()); + cell.collect_called_cells (called_cells); + + db::Connectivity device_conn = get_connectivity (layout, layers); + + db::hier_clusters device_clusters; + device_clusters.build (layout, cell, shape_iter_flags, device_conn); + + for (std::set::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) { + + m_cell_index = *ci; + + mp_circuit = new db::Circuit (); + mp_circuit->set_cell_index (*ci); + mp_circuit->set_name (layout.cell_name (*ci)); + m_netlist->add_circuit (mp_circuit); + + db::connected_clusters cc = device_clusters.clusters_per_cell (*ci); + for (db::connected_clusters::all_iterator c = cc.begin_all (); !c.at_end(); ++c) { + + if (! cc.is_root (*c)) { + continue; + } + + // build layer geometry from the cluster found + + std::vector layer_geometry; + layer_geometry.resize (layers.size ()); + + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + db::Region &r = layer_geometry [l - layers.begin ()]; + for (db::recursive_cluster_shape_iterator si (device_clusters, *l, *ci, *c); ! si.at_end(); ++si) { + insert_into_region (*si, si.trans (), r); + } + } + + extract_devices (layer_geometry); + + } + + } +} + +void NetlistDeviceExtractor::create_device_classes () +{ + // .. the default implementation does nothing .. +} + +db::Connectivity NetlistDeviceExtractor::get_connectivity (const db::Layout & /*layout*/, const std::vector & /*layers*/) const +{ + // .. the default implementation does nothing .. + return db::Connectivity (); +} + +void NetlistDeviceExtractor::extract_devices (const std::vector & /*layer_geometry*/) +{ + // .. the default implementation does nothing .. +} + +void NetlistDeviceExtractor::register_device_class (DeviceClass *device_class) +{ + tl_assert (m_netlist.get () != 0); + m_netlist->add_device_class (device_class); + m_device_classes.push_back (device_class); +} + +Device *NetlistDeviceExtractor::create_device (unsigned int device_class_index) +{ + tl_assert (mp_circuit != 0); + tl_assert (device_class_index < m_device_classes.size ()); + Device *device = new Device (m_device_classes[device_class_index], tl::to_string (++m_device_name_index)); + mp_circuit->add_device (device); + return device; +} + +void NetlistDeviceExtractor::define_port (Device *device, size_t port_id, size_t layer_index, const db::Polygon &polygon) +{ + tl_assert (mp_layout != 0); + + // Build a property set for the DevicePortProperty + db::PropertiesRepository::properties_set ps; + tl::Variant &v = ps.insert (std::make_pair (m_propname_id, tl::Variant ()))->second; + v = tl::Variant (new db::DevicePortProperty (db::NetPortRef (device, port_id)), db::NetlistProperty::variant_class (), true); + db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); + + db::PolygonRef pr (polygon, mp_layout->shape_repository ()); + mp_layout->cell (m_cell_index).shapes (layer_index).insert (db::PolygonRefWithProperties (pr, pi)); +} + +void NetlistDeviceExtractor::define_port (Device *device, size_t port_id, size_t layer_index, const db::Box &box) +{ + define_port (device, port_id, layer_index, db::Polygon (box)); +} + +void NetlistDeviceExtractor::define_port (Device *device, size_t port_id, size_t layer_index, const db::Point &point) +{ + // NOTE: we add one DBU to the "point" to prevent it from vanishing + db::Vector dv (1, 1); + define_port (device, port_id, layer_index, db::Polygon (db::Box (point - dv, point + dv))); +} + +} diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index b486b4e23..cf585e3e6 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -33,6 +33,12 @@ namespace db { +/** + * @brief Implements the device extraction for a specific setup + * + * This class can be reimplemented to provide the basic algorithms for + * device extraction. See the virtual methods below. + */ class DB_PUBLIC NetlistDeviceExtractor : public gsi::ObjectBase { @@ -58,6 +64,9 @@ public: /** * @brief Performs the extraction * + * layout and cell specify the layout and the top cell from which to perform the + * extraction. + * * The netlist will be filled with circuits (unless not present yet) to represent the * cells from the layout. * @@ -67,15 +76,10 @@ public: * the nets later to associate nets with device ports. * * The definition of the input layers is device class specific. + * + * NOTE: The extractor expects "PolygonRef" type layers. */ - void extract (db::Layout *layout, const std::vector &layers); - - /** - * @brief Checks the input layers - * This method shall raise an error, if the input layer are not properly defined (e.g. - * too few etc.) - */ - virtual void check_input_layers (db::Layout *layout, const std::vector &layers) const; + void extract (Layout &layout, Cell &cell, const std::vector &layers); /** * @brief Creates the device classes @@ -87,8 +91,10 @@ public: /** * @brief Gets the connectivity object used to extract the device geometry + * This method shall raise an error, if the input layer are not properly defined (e.g. + * too few etc.) */ - virtual db::Connectivity get_connectivity (const std::vector &layers) const; + virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector &layers) const; /** * @brief Extracts the devices from the given shape cluster @@ -143,10 +149,13 @@ protected: } private: + tl::weak_ptr m_netlist; db::Layout *mp_layout; + db::properties_id_type m_propname_id; db::cell_index_type m_cell_index; - db::Netlist *mp_netlist; db::Circuit *mp_circuit; + std::vector m_device_classes; + unsigned int m_device_name_index; }; } From 33e2fb8dc174cfa59e46a827acd87876ea638af3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Dec 2018 02:17:41 +0100 Subject: [PATCH 107/335] WIP: refinement of device extraction algorithm --- src/db/db/dbNetlistDeviceExtractor.cc | 34 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 8e5b30908..686fa52f3 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -46,6 +46,7 @@ void NetlistDeviceExtractor::initialize (db::Netlist *nl) m_device_name_index = 0; m_propname_id = 0; m_netlist.reset (nl); + create_device_classes (); } @@ -60,32 +61,54 @@ void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const db::ShapeIterator::flags_type shape_iter_flags = db::ShapeIterator::Polygons; mp_layout = &layout; + // port properties are kept in property index 0 m_propname_id = mp_layout->properties_repository ().prop_name_id (tl::Variant (int (0))); tl_assert (m_netlist.get () != 0); + // build a cell-id-to-circuit lookup table + std::map circuits_by_cell; + for (db::Netlist::circuit_iterator c = m_netlist->begin_circuits (); c != m_netlist->end_circuits (); ++c) { + circuits_by_cell.insert (std::make_pair (c->cell_index (), c.operator-> ())); + } + + // collect the cells below the top cell std::set called_cells; called_cells.insert (cell.cell_index ()); cell.collect_called_cells (called_cells); + // build the device clusters db::Connectivity device_conn = get_connectivity (layout, layers); - db::hier_clusters device_clusters; device_clusters.build (layout, cell, shape_iter_flags, device_conn); + // for each cell investigate the clusters for (std::set::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) { m_cell_index = *ci; - mp_circuit = new db::Circuit (); - mp_circuit->set_cell_index (*ci); - mp_circuit->set_name (layout.cell_name (*ci)); - m_netlist->add_circuit (mp_circuit); + std::map::const_iterator c2c = circuits_by_cell.find (*ci); + if (c2c != circuits_by_cell.end ()) { + // reuse existing circuit + mp_circuit = c2c->second; + + } else { + + // create a new circuit for this cell + mp_circuit = new db::Circuit (); + mp_circuit->set_cell_index (*ci); + mp_circuit->set_name (layout.cell_name (*ci)); + m_netlist->add_circuit (mp_circuit); + + } + + // investigate each cluster db::connected_clusters cc = device_clusters.clusters_per_cell (*ci); for (db::connected_clusters::all_iterator c = cc.begin_all (); !c.at_end(); ++c) { + // take only root clusters - others have upward connections and are not "whole" if (! cc.is_root (*c)) { continue; } @@ -102,6 +125,7 @@ void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const } } + // do the actual device extraction extract_devices (layer_geometry); } From a7a2eea905e5331e65052ef3be88394b1d0f88ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Dec 2018 19:41:29 +0100 Subject: [PATCH 108/335] WIP: MOS3 and MOS4 devices --- src/db/db/dbNetlistDeviceClasses.cc | 57 +++++++++++++++++++++++++++-- src/db/db/dbNetlistDeviceClasses.h | 27 ++++++++++++-- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index 47ccc5702..b68bdd32e 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -158,6 +158,7 @@ bool DeviceClassDiode::combine_devices (Device *a, Device *b) const const db::Net *nb1 = b->net_for_port (0); const db::Net *nb2 = b->net_for_port (1); + // only parallel diodes can be combined and their areas will add if ((na1 == nb1 && na2 == nb2) || (na1 == nb2 && na2 == nb1)) { a->set_parameter_value (0, a->parameter_value (0) + b->parameter_value (0)); @@ -172,9 +173,9 @@ bool DeviceClassDiode::combine_devices (Device *a, Device *b) const } // ------------------------------------------------------------------------------------ -// DeviceClassInductor implementation +// DeviceClassMOS3Transistor implementation -DeviceClassMOSTransistor::DeviceClassMOSTransistor () +DeviceClassMOS3Transistor::DeviceClassMOS3Transistor () { add_port_definition (db::DevicePortDefinition ("S", "Source")); add_port_definition (db::DevicePortDefinition ("G", "Gate")); @@ -186,7 +187,7 @@ DeviceClassMOSTransistor::DeviceClassMOSTransistor () add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0)); } -bool DeviceClassMOSTransistor::combine_devices (Device *a, Device *b) const +bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const { const db::Net *nas = a->net_for_port (0); const db::Net *nag = a->net_for_port (1); @@ -217,4 +218,54 @@ bool DeviceClassMOSTransistor::combine_devices (Device *a, Device *b) const return false; } +// ------------------------------------------------------------------------------------ +// DeviceClassMOS4Transistor implementation + +DeviceClassMOS4Transistor::DeviceClassMOS4Transistor () +{ + add_port_definition (db::DevicePortDefinition ("S", "Source")); + add_port_definition (db::DevicePortDefinition ("G", "Gate")); + add_port_definition (db::DevicePortDefinition ("D", "Drain")); + add_port_definition (db::DevicePortDefinition ("B", "Bulk")); + + add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0)); +} + +bool DeviceClassMOS4Transistor::combine_devices (Device *a, Device *b) const +{ + const db::Net *nas = a->net_for_port (0); + const db::Net *nag = a->net_for_port (1); + const db::Net *nad = a->net_for_port (2); + const db::Net *nab = a->net_for_port (3); + const db::Net *nbs = b->net_for_port (0); + const db::Net *nbg = b->net_for_port (1); + const db::Net *nbd = b->net_for_port (2); + const db::Net *nbb = b->net_for_port (3); + + // parallel transistors can be combined into one + if (((nas == nbs && nad == nbd) || (nas == nbd && nad == nbs)) && nag == nbg && nab == nbb) { + + // for combination the gate length must be identical + if (fabs (a->parameter_value (0) - b->parameter_value (0)) < 1e-6) { + + a->set_parameter_value (1, a->parameter_value (1) + b->parameter_value (1)); + a->set_parameter_value (2, a->parameter_value (2) + b->parameter_value (2)); + a->set_parameter_value (3, a->parameter_value (3) + b->parameter_value (3)); + b->connect_port (0, 0); + b->connect_port (1, 0); + b->connect_port (2, 0); + b->connect_port (3, 0); + + return true; + + } + + } + + return false; +} + } diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index b90edd82c..76184f152 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -123,20 +123,39 @@ public: }; /** - * @brief A basic MOSFET device class + * @brief A basic MOSFET device class with three terminals * A MOSFET defines four parameters: "W" for the gate width in micrometers, "L" for the gate length in micrometers, * "AS" for the source area and "AD" for the drain area. * The MOSFET device defines three ports, "S", "D" and "G" for source, drain and gate. */ -class DB_PUBLIC DeviceClassMOSTransistor +class DB_PUBLIC DeviceClassMOS3Transistor : public db::DeviceClass { public: - DeviceClassMOSTransistor (); + DeviceClassMOS3Transistor (); virtual db::DeviceClass *clone () const { - return new DeviceClassMOSTransistor (*this); + return new DeviceClassMOS3Transistor (*this); + } + + virtual bool combine_devices (Device *a, Device *b) const; +}; + +/** + * @brief A basic MOSFET device class with four terminals + * The four-terminal MOSFET behaves identical to the three-terminal one but adds one more + * port for the bulk. + */ +class DB_PUBLIC DeviceClassMOS4Transistor + : public db::DeviceClass +{ +public: + DeviceClassMOS4Transistor (); + + virtual db::DeviceClass *clone () const + { + return new DeviceClassMOS4Transistor (*this); } virtual bool combine_devices (Device *a, Device *b) const; From 9c9d99da7c3cd4ee16832c12ac6f8cc4f3187916 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Dec 2018 19:45:34 +0100 Subject: [PATCH 109/335] WIP: introduced device combination support hints. --- src/db/db/dbNetlist.cc | 8 ++++++-- src/db/db/dbNetlist.h | 16 ++++++++++++++++ src/db/db/dbNetlistDeviceClasses.h | 5 +++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 39c04268d..0d7e1cc5a 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -812,8 +812,12 @@ void Circuit::combine_devices () tl_assert (netlist () != 0); for (Netlist::device_class_iterator dc = netlist ()->begin_device_classes (); dc != netlist ()->end_device_classes (); ++dc) { - combine_parallel_devices (*dc); - combine_serial_devices (*dc); + if (dc->supports_parallel_combination ()) { + combine_parallel_devices (*dc); + } + if (dc->supports_serial_combination ()) { + combine_serial_devices (*dc); + } } } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index b3d911144..160d97634 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -1431,6 +1431,22 @@ public: return false; } + /** + * @brief Returns true if the device class supports device combination in parallel mode + */ + virtual bool supports_parallel_combination () const + { + return false; + } + + /** + * @brief Returns true if the device class supports device combination in serial mode + */ + virtual bool supports_serial_combination () const + { + return false; + } + private: friend class Netlist; diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index 76184f152..a338193f1 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -40,6 +40,8 @@ public: virtual void parallel (Device *a, Device *b) const = 0; virtual void serial (Device *a, Device *b) const = 0; + virtual bool supports_parallel_combination () const { return true; } + virtual bool supports_serial_combination () const { return true; } }; /** @@ -120,6 +122,7 @@ public: } virtual bool combine_devices (Device *a, Device *b) const; + virtual bool supports_parallel_combination () const { return true; } }; /** @@ -140,6 +143,7 @@ public: } virtual bool combine_devices (Device *a, Device *b) const; + virtual bool supports_parallel_combination () const { return true; } }; /** @@ -159,6 +163,7 @@ public: } virtual bool combine_devices (Device *a, Device *b) const; + virtual bool supports_parallel_combination () const { return true; } }; } From 4f8416766c8ba6537a929f1bacd29c48f58ce883 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Dec 2018 20:19:37 +0100 Subject: [PATCH 110/335] WIP: renamed port -> terminal for devices. This is correct technical term. A port is a two-terminal entity. --- src/db/db/dbNetlist.cc | 146 ++++++++--------- src/db/db/dbNetlist.h | 166 ++++++++++---------- src/db/db/dbNetlistDeviceClasses.cc | 104 ++++++------ src/db/db/dbNetlistDeviceClasses.h | 22 +-- src/db/db/dbNetlistDeviceExtractor.cc | 16 +- src/db/db/dbNetlistDeviceExtractor.h | 22 +-- src/db/db/dbNetlistProperty.cc | 46 +++--- src/db/db/dbNetlistProperty.h | 32 ++-- src/db/db/gsiDeclDbNetlist.cc | 148 ++++++++--------- src/db/db/gsiDeclDbNetlistProperty.cc | 34 ++-- src/db/unit_tests/dbNetlistPropertyTests.cc | 20 +-- src/db/unit_tests/dbNetlistTests.cc | 148 ++++++++--------- testdata/ruby/dbNetlist.rb | 58 +++---- testdata/ruby/dbNetlistProperty.rb | 26 +-- 14 files changed, 494 insertions(+), 494 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 0d7e1cc5a..5ee840c20 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -52,9 +52,9 @@ Device::Device () Device::~Device () { - for (std::vector::const_iterator p = m_port_refs.begin (); p != m_port_refs.end (); ++p) { - if (*p != Net::port_iterator () && (*p)->net ()) { - (*p)->net ()->erase_port (*p); + for (std::vector::const_iterator t = m_terminal_refs.begin (); t != m_terminal_refs.end (); ++t) { + if (*t != Net::terminal_iterator () && (*t)->net ()) { + (*t)->net ()->erase_terminal (*t); } } } @@ -84,41 +84,41 @@ void Device::set_name (const std::string &n) m_name = n; } -void Device::set_port_ref_for_port (size_t port_id, Net::port_iterator iter) +void Device::set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter) { - if (m_port_refs.size () < port_id + 1) { - m_port_refs.resize (port_id + 1, Net::port_iterator ()); + if (m_terminal_refs.size () < terminal_id + 1) { + m_terminal_refs.resize (terminal_id + 1, Net::terminal_iterator ()); } - m_port_refs [port_id] = iter; + m_terminal_refs [terminal_id] = iter; } -const Net *Device::net_for_port (size_t port_id) const +const Net *Device::net_for_terminal (size_t terminal_id) const { - if (port_id < m_port_refs.size ()) { - Net::port_iterator p = m_port_refs [port_id]; - if (p != Net::port_iterator ()) { + if (terminal_id < m_terminal_refs.size ()) { + Net::terminal_iterator p = m_terminal_refs [terminal_id]; + if (p != Net::terminal_iterator ()) { return p->net (); } } return 0; } -void Device::connect_port (size_t port_id, Net *net) +void Device::connect_terminal (size_t terminal_id, Net *net) { - if (net_for_port (port_id) == net) { + if (net_for_terminal (terminal_id) == net) { return; } - if (port_id < m_port_refs.size ()) { - Net::port_iterator p = m_port_refs [port_id]; - if (p != Net::port_iterator () && p->net ()) { - p->net ()->erase_port (p); + if (terminal_id < m_terminal_refs.size ()) { + Net::terminal_iterator p = m_terminal_refs [terminal_id]; + if (p != Net::terminal_iterator () && p->net ()) { + p->net ()->erase_terminal (p); } - m_port_refs [port_id] = Net::port_iterator (); + m_terminal_refs [terminal_id] = Net::terminal_iterator (); } if (net) { - net->add_port (NetPortRef (this, port_id)); + net->add_terminal (NetTerminalRef (this, terminal_id)); } } @@ -244,48 +244,48 @@ void SubCircuit::connect_pin (size_t pin_id, Net *net) } // -------------------------------------------------------------------------------- -// NetPortRef class implementation +// NetTerminalRef class implementation -NetPortRef::NetPortRef () - : m_port_id (0), mp_device (0), mp_net (0) +NetTerminalRef::NetTerminalRef () + : m_terminal_id (0), mp_device (0), mp_net (0) { // .. nothing yet .. } -NetPortRef::NetPortRef (Device *device, size_t port_id) - : m_port_id (port_id), mp_device (device), mp_net (0) +NetTerminalRef::NetTerminalRef (Device *device, size_t terminal_id) + : m_terminal_id (terminal_id), mp_device (device), mp_net (0) { // .. nothing yet .. } -NetPortRef::NetPortRef (const NetPortRef &other) - : m_port_id (other.m_port_id), mp_device (other.mp_device), mp_net (0) +NetTerminalRef::NetTerminalRef (const NetTerminalRef &other) + : m_terminal_id (other.m_terminal_id), mp_device (other.mp_device), mp_net (0) { // .. nothing yet .. } -NetPortRef &NetPortRef::operator= (const NetPortRef &other) +NetTerminalRef &NetTerminalRef::operator= (const NetTerminalRef &other) { if (this != &other) { - m_port_id = other.m_port_id; + m_terminal_id = other.m_terminal_id; mp_device = other.mp_device; } return *this; } -const DevicePortDefinition * -NetPortRef::port_def () const +const DeviceTerminalDefinition * +NetTerminalRef::terminal_def () const { const DeviceClass *dc = device_class (); if (dc) { - return dc->port_definition (m_port_id); + return dc->terminal_definition (m_terminal_id); } else { return 0; } } const DeviceClass * -NetPortRef::device_class () const +NetTerminalRef::device_class () const { return mp_device ? mp_device->device_class () : 0; } @@ -366,8 +366,8 @@ Net &Net::operator= (const Net &other) add_pin (*i); } - for (const_port_iterator i = other.begin_ports (); i != other.end_ports (); ++i) { - add_port (*i); + for (const_terminal_iterator i = other.begin_terminals (); i != other.end_terminals (); ++i) { + add_terminal (*i); } } @@ -384,8 +384,8 @@ void Net::clear () m_name.clear (); m_cluster_id = 0; - while (! m_ports.empty ()) { - erase_port (begin_ports ()); + while (! m_terminals.empty ()) { + erase_terminal (begin_terminals ()); } while (! m_pins.empty ()) { @@ -428,24 +428,24 @@ void Net::erase_pin (pin_iterator iter) m_pins.erase (iter); } -void Net::add_port (const NetPortRef &port) +void Net::add_terminal (const NetTerminalRef &terminal) { - if (! port.device ()) { + if (! terminal.device ()) { return; } - m_ports.push_back (port); - NetPortRef &new_port = m_ports.back (); - new_port.set_net (this); - new_port.device ()->set_port_ref_for_port (new_port.port_id (), --m_ports.end ()); + m_terminals.push_back (terminal); + NetTerminalRef &new_terminal = m_terminals.back (); + new_terminal.set_net (this); + new_terminal.device ()->set_terminal_ref_for_terminal (new_terminal.terminal_id (), --m_terminals.end ()); } -void Net::erase_port (port_iterator iter) +void Net::erase_terminal (terminal_iterator iter) { if (iter->device ()) { - iter->device ()->set_port_ref_for_port (iter->port_id (), port_iterator ()); + iter->device ()->set_terminal_ref_for_terminal (iter->terminal_id (), terminal_iterator ()); } - m_ports.erase (iter); + m_terminals.erase (iter); } void Net::set_circuit (Circuit *circuit) @@ -500,10 +500,10 @@ Circuit &Circuit::operator= (const Circuit &other) n->set_name (i->name ()); add_net (n); - for (Net::const_port_iterator p = i->begin_ports (); p != i->end_ports (); ++p) { + for (Net::const_terminal_iterator p = i->begin_terminals (); p != i->end_terminals (); ++p) { std::map::const_iterator m = device_table.find (p->device ()); tl_assert (m != device_table.end ()); - n->add_port (NetPortRef (m->second, p->port_id ())); + n->add_terminal (NetTerminalRef (m->second, p->terminal_id ())); } for (Net::const_pin_iterator p = i->begin_pins (); p != i->end_pins (); ++p) { @@ -671,10 +671,10 @@ static void check_device_before_remove (db::Circuit *c, const db::Device *d) if (d->device_class () != 0) { throw tl::Exception (tl::to_string (tr ("Internal error: No device class after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name ()); } - const std::vector &pd = d->device_class ()->port_definitions (); - for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { - if (d->net_for_port (p->id ()) != 0) { - throw tl::Exception (tl::to_string (tr ("Internal error: Port still connected after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name () + ", port=" + p->name ()); + const std::vector &pd = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (d->net_for_terminal (p->id ()) != 0) { + throw tl::Exception (tl::to_string (tr ("Internal error: Terminal still connected after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name () + ", terminal=" + p->name ()); } } } @@ -693,9 +693,9 @@ void Circuit::combine_parallel_devices (const db::DeviceClass &cls) } key_type k; - const std::vector &ports = cls.port_definitions (); - for (std::vector::const_iterator p = ports.begin (); p != ports.end (); ++p) { - const db::Net *n = d->net_for_port (p->id ()); + const std::vector &terminals = cls.terminal_definitions (); + for (std::vector::const_iterator p = terminals.begin (); p != terminals.end (); ++p) { + const db::Net *n = d->net_for_terminal (p->id ()); if (n) { k.push_back (n); } @@ -734,22 +734,22 @@ static std::pair attached_two_devices (db::Net &net, db::Device *d1 = 0, *d2 = 0; - Net::port_iterator p = net.begin_ports (); - if (p == net.end_ports () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { + Net::terminal_iterator p = net.begin_terminals (); + if (p == net.end_terminals () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { return std::make_pair ((db::Device *) 0, (db::Device *) 0); } else { d1 = p->device (); } ++p; - if (p == net.end_ports () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { + if (p == net.end_terminals () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { return std::make_pair ((db::Device *) 0, (db::Device *) 0); } else { d2 = p->device (); } ++p; - if (p != net.end_ports () || d1 == d2 || !d1 || !d2) { + if (p != net.end_terminals () || d1 == d2 || !d1 || !d2) { return std::make_pair ((db::Device *) 0, (db::Device *) 0); } else { return std::make_pair (d1, d2); @@ -778,14 +778,14 @@ void Circuit::combine_serial_devices (const db::DeviceClass &cls) std::vector other_nets; - const std::vector &ports = cls.port_definitions (); - for (std::vector::const_iterator p = ports.begin (); p != ports.end (); ++p) { + const std::vector &terminals = cls.terminal_definitions (); + for (std::vector::const_iterator p = terminals.begin (); p != terminals.end (); ++p) { db::Net *on; - on = dd.first->net_for_port (p->id ()); + on = dd.first->net_for_terminal (p->id ()); if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) { other_nets.push_back (on); } - on = dd.second->net_for_port (p->id ()); + on = dd.second->net_for_terminal (p->id ()); if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) { other_nets.push_back (on); } @@ -794,7 +794,7 @@ void Circuit::combine_serial_devices (const db::DeviceClass &cls) std::sort (other_nets.begin (), other_nets.end ()); other_nets.erase (std::unique (other_nets.begin (), other_nets.end ()), other_nets.end ()); - if (other_nets.size () <= cls.port_definitions().size ()) { + if (other_nets.size () <= cls.terminal_definitions().size ()) { // found a combination candidate if (cls.combine_devices (dd.first, dd.second)) { @@ -839,29 +839,29 @@ DeviceClass::DeviceClass (const DeviceClass &other) DeviceClass &DeviceClass::operator= (const DeviceClass &other) { if (this != &other) { - m_port_definitions = other.m_port_definitions; + m_terminal_definitions = other.m_terminal_definitions; m_name = other.m_name; m_description = other.m_description; } return *this; } -const DevicePortDefinition &DeviceClass::add_port_definition (const DevicePortDefinition &pd) +const DeviceTerminalDefinition &DeviceClass::add_terminal_definition (const DeviceTerminalDefinition &pd) { - m_port_definitions.push_back (pd); - m_port_definitions.back ().set_id (m_port_definitions.size () - 1); - return m_port_definitions.back (); + m_terminal_definitions.push_back (pd); + m_terminal_definitions.back ().set_id (m_terminal_definitions.size () - 1); + return m_terminal_definitions.back (); } -void DeviceClass::clear_port_definitions () +void DeviceClass::clear_terminal_definitions () { - m_port_definitions.clear (); + m_terminal_definitions.clear (); } -const DevicePortDefinition *DeviceClass::port_definition (size_t id) const +const DeviceTerminalDefinition *DeviceClass::terminal_definition (size_t id) const { - if (id < m_port_definitions.size ()) { - return & m_port_definitions [id]; + if (id < m_terminal_definitions.size ()) { + return & m_terminal_definitions [id]; } else { return 0; } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 160d97634..d6dcbad75 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -44,55 +44,55 @@ class SubCircuit; class Pin; class Device; class DeviceClass; -class DevicePortDefinition; +class DeviceTerminalDefinition; class Netlist; class Net; /** - * @brief A reference to a port of a device + * @brief A reference to a terminal of a device * - * A port must always refer to a device inside the current circuit. + * A terminal must always refer to a device inside the current circuit. */ -class DB_PUBLIC NetPortRef +class DB_PUBLIC NetTerminalRef { public: /** * @brief Default constructor */ - NetPortRef (); + NetTerminalRef (); /** * @brief Creates a pin reference to the given pin of the current circuit */ - NetPortRef (Device *device, size_t port_id); + NetTerminalRef (Device *device, size_t terminal_id); /** * @brief Copy constructor */ - NetPortRef (const NetPortRef &other); + NetTerminalRef (const NetTerminalRef &other); /** * @brief Assignment */ - NetPortRef &operator= (const NetPortRef &other); + NetTerminalRef &operator= (const NetTerminalRef &other); /** * @brief Comparison */ - bool operator< (const NetPortRef &other) const + bool operator< (const NetTerminalRef &other) const { if (mp_device != other.mp_device) { return mp_device < other.mp_device; } - return m_port_id < other.m_port_id; + return m_terminal_id < other.m_terminal_id; } /** * @brief Equality */ - bool operator== (const NetPortRef &other) const + bool operator== (const NetTerminalRef &other) const { - return (mp_device == other.mp_device && m_port_id == other.m_port_id); + return (mp_device == other.mp_device && m_terminal_id == other.m_terminal_id); } /** @@ -112,19 +112,19 @@ public: } /** - * @brief Gets the port index + * @brief Gets the terminal index */ - size_t port_id () const + size_t terminal_id () const { - return m_port_id; + return m_terminal_id; } /** - * @brief Gets the port definition + * @brief Gets the terminal definition * - * Returns 0 if the port is not a valid port reference. + * Returns 0 if the terminal is not a valid terminal reference. */ - const DevicePortDefinition *port_def () const; + const DeviceTerminalDefinition *terminal_def () const; /** * @brief Returns the device class @@ -132,7 +132,7 @@ public: const DeviceClass *device_class () const; /** - * @brief Gets the net the port lives in + * @brief Gets the net the terminal lives in */ Net *net () { @@ -140,7 +140,7 @@ public: } /** - * @brief Gets the net the port lives in (const version) + * @brief Gets the net the terminal lives in (const version) */ const Net *net () const { @@ -150,12 +150,12 @@ public: private: friend class Net; - size_t m_port_id; + size_t m_terminal_id; Device *mp_device; Net *mp_net; /** - * @brief Sets the net the port lives in + * @brief Sets the net the terminal lives in */ void set_net (Net *net) { @@ -271,7 +271,7 @@ private: Net *mp_net; /** - * @brief Sets the net the port lives in + * @brief Sets the net the terminal lives in */ void set_net (Net *net) { @@ -282,15 +282,15 @@ private: /** * @brief A net * - * A net connects ports of devices and pins or circuits + * A net connects terminals of devices and pins or circuits */ class DB_PUBLIC Net : public tl::Object { public: - typedef std::list port_list; - typedef port_list::const_iterator const_port_iterator; - typedef port_list::iterator port_iterator; + typedef std::list terminal_list; + typedef terminal_list::const_iterator const_terminal_iterator; + typedef terminal_list::iterator terminal_iterator; typedef std::list pin_list; typedef pin_list::const_iterator const_pin_iterator; typedef pin_list::iterator pin_iterator; @@ -411,45 +411,45 @@ public: } /** - * @brief Adds a port to this net + * @brief Adds a terminal to this net */ - void add_port (const NetPortRef &port); + void add_terminal (const NetTerminalRef &terminal); /** - * @brief Erases the given port from this net + * @brief Erases the given terminal from this net */ - void erase_port (port_iterator iter); + void erase_terminal (terminal_iterator iter); /** - * @brief Begin iterator for the ports of the net (const version) + * @brief Begin iterator for the terminals of the net (const version) */ - const_port_iterator begin_ports () const + const_terminal_iterator begin_terminals () const { - return m_ports.begin (); + return m_terminals.begin (); } /** - * @brief End iterator for the ports of the net (const version) + * @brief End iterator for the terminals of the net (const version) */ - const_port_iterator end_ports () const + const_terminal_iterator end_terminals () const { - return m_ports.end (); + return m_terminals.end (); } /** - * @brief Begin iterator for the ports of the net (non-const version) + * @brief Begin iterator for the terminals of the net (non-const version) */ - port_iterator begin_ports () + terminal_iterator begin_terminals () { - return m_ports.begin (); + return m_terminals.begin (); } /** - * @brief End iterator for the ports of the net (non-const version) + * @brief End iterator for the terminals of the net (non-const version) */ - port_iterator end_ports () + terminal_iterator end_terminals () { - return m_ports.end (); + return m_terminals.end (); } /** @@ -457,13 +457,13 @@ public: */ bool floating () const { - return (m_pins.size () + m_ports.size ()) < 2; + return (m_pins.size () + m_terminals.size ()) < 2; } private: friend class Circuit; - port_list m_ports; + terminal_list m_terminals; pin_list m_pins; std::string m_name; size_t m_cluster_id; @@ -579,27 +579,27 @@ public: } /** - * @brief Gets the net attached to a specific port + * @brief Gets the net attached to a specific terminal * Returns 0 if no net is attached. */ - const Net *net_for_port (size_t port_id) const; + const Net *net_for_terminal (size_t terminal_id) const; /** - * @brief Gets the net attached to a specific port (non-const version) + * @brief Gets the net attached to a specific terminal (non-const version) * Returns 0 if no net is attached. */ - Net *net_for_port (size_t port_id) + Net *net_for_terminal (size_t terminal_id) { - return const_cast (((const Device *) this)->net_for_port (port_id)); + return const_cast (((const Device *) this)->net_for_terminal (terminal_id)); } /** - * @brief Connects the given port to the given net - * If the net is 0 the port is disconnected. - * If non-null, a NetPortRef object will be inserted into the - * net and connected with the given port. + * @brief Connects the given terminal to the given net + * If the net is 0 the terminal is disconnected. + * If non-null, a NetTerminalRef object will be inserted into the + * net and connected with the given terminal. */ - void connect_port (size_t port_id, Net *net); + void connect_terminal (size_t terminal_id, Net *net); /** * @brief Gets the value for the parameter with the given ID @@ -617,13 +617,13 @@ private: DeviceClass *mp_device_class; std::string m_name; - std::vector m_port_refs; + std::vector m_terminal_refs; std::vector m_parameters; /** - * @brief Sets the port reference for a specific port + * @brief Sets the terminal reference for a specific terminal */ - void set_port_ref_for_port (size_t port_id, Net::port_iterator iter); + void set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter); /** * @brief Sets the device class @@ -1105,31 +1105,31 @@ private: }; /** - * @brief A device port definition + * @brief A device terminal definition */ -class DB_PUBLIC DevicePortDefinition +class DB_PUBLIC DeviceTerminalDefinition { public: /** - * @brief Creates an empty device port definition + * @brief Creates an empty device terminal definition */ - DevicePortDefinition () + DeviceTerminalDefinition () : m_name (), m_description (), m_id (0) { // .. nothing yet .. } /** - * @brief Creates a device port definition with the given name and description + * @brief Creates a device terminal definition with the given name and description */ - DevicePortDefinition (const std::string &name, const std::string &description) + DeviceTerminalDefinition (const std::string &name, const std::string &description) : m_name (name), m_description (description), m_id (0) { // .. nothing yet .. } /** - * @brief Gets the port name + * @brief Gets the terminal name */ const std::string &name () const { @@ -1137,7 +1137,7 @@ public: } /** - * @brief Sets the port name + * @brief Sets the terminal name */ void set_name (const std::string &n) { @@ -1145,7 +1145,7 @@ public: } /** - * @brief Gets the port description + * @brief Gets the terminal description */ const std::string &description () const { @@ -1153,7 +1153,7 @@ public: } /** - * @brief Sets the port description + * @brief Sets the terminal description */ void set_description (const std::string &d) { @@ -1161,7 +1161,7 @@ public: } /** - * @brief Gets the port ID + * @brief Gets the terminal ID */ size_t id () const { @@ -1282,7 +1282,7 @@ class DB_PUBLIC DeviceClass : public gsi::ObjectBase, public tl::Object, public tl::UniqueId { public: - typedef size_t port_id_type; + typedef size_t terminal_id_type; /** * @brief Constructor @@ -1359,31 +1359,31 @@ public: } /** - * @brief Gets the port definitions + * @brief Gets the terminal definitions * - * The port definitions indicate what ports the device offers. - * The number of ports is constant per class. The index of the port - * is used as an ID of the port, hence the order must be static. + * The terminal definitions indicate what terminals the device offers. + * The number of terminals is constant per class. The index of the terminal + * is used as an ID of the terminal, hence the order must be static. */ - const std::vector &port_definitions () const + const std::vector &terminal_definitions () const { - return m_port_definitions; + return m_terminal_definitions; } /** - * @brief Adds a port definition + * @brief Adds a terminal definition */ - const DevicePortDefinition &add_port_definition (const DevicePortDefinition &pd); + const DeviceTerminalDefinition &add_terminal_definition (const DeviceTerminalDefinition &pd); /** - * @brief Clears the port definition + * @brief Clears the terminal definition */ - void clear_port_definitions (); + void clear_terminal_definitions (); /** - * @brief Gets the port definition from the ID + * @brief Gets the terminal definition from the ID */ - const DevicePortDefinition *port_definition (size_t id) const; + const DeviceTerminalDefinition *terminal_definition (size_t id) const; /** * @brief Gets the parameter definitions @@ -1451,7 +1451,7 @@ private: friend class Netlist; std::string m_name, m_description; - std::vector m_port_definitions; + std::vector m_terminal_definitions; std::vector m_parameter_definitions; db::Netlist *mp_netlist; diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index b68bdd32e..ce09bec1f 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -26,14 +26,14 @@ namespace db { // ------------------------------------------------------------------------------------ -// DeviceClassTwoPortDevice implementation +// DeviceClassTwoTerminalDevice implementation -bool DeviceClassTwoPortDevice::combine_devices (Device *a, Device *b) const +bool DeviceClassTwoTerminalDevice::combine_devices (Device *a, Device *b) const { - db::Net *na1 = a->net_for_port (0); - db::Net *na2 = a->net_for_port (1); - db::Net *nb1 = b->net_for_port (0); - db::Net *nb2 = b->net_for_port (1); + db::Net *na1 = a->net_for_terminal (0); + db::Net *na2 = a->net_for_terminal (1); + db::Net *nb1 = b->net_for_terminal (0); + db::Net *nb2 = b->net_for_terminal (1); bool res = true; @@ -45,19 +45,19 @@ bool DeviceClassTwoPortDevice::combine_devices (Device *a, Device *b) const // serial a(B) to b(A or B) serial (a, b); - a->connect_port (1, (na2 == nb1 ? nb2 : nb1)); + a->connect_terminal (1, (na2 == nb1 ? nb2 : nb1)); } else if (na1 == nb1 || na1 == nb2) { // serial a(A) to b(A or B) serial (a, b); - a->connect_port (0, (na1 == nb1 ? nb2 : nb1)); + a->connect_terminal (0, (na1 == nb1 ? nb2 : nb1)); } if (res) { - b->connect_port (0, 0); - b->connect_port (1, 0); + b->connect_terminal (0, 0); + b->connect_terminal (1, 0); return true; } else { return false; @@ -70,8 +70,8 @@ bool DeviceClassTwoPortDevice::combine_devices (Device *a, Device *b) const DeviceClassResistor::DeviceClassResistor () { - add_port_definition (db::DevicePortDefinition ("A", "Port A")); - add_port_definition (db::DevicePortDefinition ("B", "Port B")); + add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); + add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B")); add_parameter_definition (db::DeviceParameterDefinition ("R", "Resistance (Ohm)", 0.0)); } @@ -95,8 +95,8 @@ void DeviceClassResistor::serial (Device *a, Device *b) const DeviceClassCapacitor::DeviceClassCapacitor () { - add_port_definition (db::DevicePortDefinition ("A", "Port A")); - add_port_definition (db::DevicePortDefinition ("B", "Port B")); + add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); + add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B")); add_parameter_definition (db::DeviceParameterDefinition ("C", "Capacitance (Farad)", 0.0)); } @@ -120,8 +120,8 @@ void DeviceClassCapacitor::parallel (Device *a, Device *b) const DeviceClassInductor::DeviceClassInductor () { - add_port_definition (db::DevicePortDefinition ("A", "Port A")); - add_port_definition (db::DevicePortDefinition ("B", "Port B")); + add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); + add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B")); add_parameter_definition (db::DeviceParameterDefinition ("L", "Inductance (Henry)", 0.0)); } @@ -145,25 +145,25 @@ void DeviceClassInductor::serial (Device *a, Device *b) const DeviceClassDiode::DeviceClassDiode () { - add_port_definition (db::DevicePortDefinition ("A", "Anode")); - add_port_definition (db::DevicePortDefinition ("C", "Cathode")); + add_terminal_definition (db::DeviceTerminalDefinition ("A", "Anode")); + add_terminal_definition (db::DeviceTerminalDefinition ("C", "Cathode")); add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0)); } bool DeviceClassDiode::combine_devices (Device *a, Device *b) const { - const db::Net *na1 = a->net_for_port (0); - const db::Net *na2 = a->net_for_port (1); - const db::Net *nb1 = b->net_for_port (0); - const db::Net *nb2 = b->net_for_port (1); + const db::Net *na1 = a->net_for_terminal (0); + const db::Net *na2 = a->net_for_terminal (1); + const db::Net *nb1 = b->net_for_terminal (0); + const db::Net *nb2 = b->net_for_terminal (1); // only parallel diodes can be combined and their areas will add if ((na1 == nb1 && na2 == nb2) || (na1 == nb2 && na2 == nb1)) { a->set_parameter_value (0, a->parameter_value (0) + b->parameter_value (0)); - b->connect_port (0, 0); - b->connect_port (1, 0); + b->connect_terminal (0, 0); + b->connect_terminal (1, 0); return true; @@ -177,9 +177,9 @@ bool DeviceClassDiode::combine_devices (Device *a, Device *b) const DeviceClassMOS3Transistor::DeviceClassMOS3Transistor () { - add_port_definition (db::DevicePortDefinition ("S", "Source")); - add_port_definition (db::DevicePortDefinition ("G", "Gate")); - add_port_definition (db::DevicePortDefinition ("D", "Drain")); + add_terminal_definition (db::DeviceTerminalDefinition ("S", "Source")); + add_terminal_definition (db::DeviceTerminalDefinition ("G", "Gate")); + add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain")); add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0)); add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0)); @@ -189,12 +189,12 @@ DeviceClassMOS3Transistor::DeviceClassMOS3Transistor () bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const { - const db::Net *nas = a->net_for_port (0); - const db::Net *nag = a->net_for_port (1); - const db::Net *nad = a->net_for_port (2); - const db::Net *nbs = b->net_for_port (0); - const db::Net *nbg = b->net_for_port (1); - const db::Net *nbd = b->net_for_port (2); + const db::Net *nas = a->net_for_terminal (0); + const db::Net *nag = a->net_for_terminal (1); + const db::Net *nad = a->net_for_terminal (2); + const db::Net *nbs = b->net_for_terminal (0); + const db::Net *nbg = b->net_for_terminal (1); + const db::Net *nbd = b->net_for_terminal (2); // parallel transistors can be combined into one if (((nas == nbs && nad == nbd) || (nas == nbd && nad == nbs)) && nag == nbg) { @@ -205,9 +205,9 @@ bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const a->set_parameter_value (1, a->parameter_value (1) + b->parameter_value (1)); a->set_parameter_value (2, a->parameter_value (2) + b->parameter_value (2)); a->set_parameter_value (3, a->parameter_value (3) + b->parameter_value (3)); - b->connect_port (0, 0); - b->connect_port (1, 0); - b->connect_port (2, 0); + b->connect_terminal (0, 0); + b->connect_terminal (1, 0); + b->connect_terminal (2, 0); return true; @@ -223,10 +223,10 @@ bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const DeviceClassMOS4Transistor::DeviceClassMOS4Transistor () { - add_port_definition (db::DevicePortDefinition ("S", "Source")); - add_port_definition (db::DevicePortDefinition ("G", "Gate")); - add_port_definition (db::DevicePortDefinition ("D", "Drain")); - add_port_definition (db::DevicePortDefinition ("B", "Bulk")); + add_terminal_definition (db::DeviceTerminalDefinition ("S", "Source")); + add_terminal_definition (db::DeviceTerminalDefinition ("G", "Gate")); + add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain")); + add_terminal_definition (db::DeviceTerminalDefinition ("B", "Bulk")); add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0)); add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0)); @@ -236,14 +236,14 @@ DeviceClassMOS4Transistor::DeviceClassMOS4Transistor () bool DeviceClassMOS4Transistor::combine_devices (Device *a, Device *b) const { - const db::Net *nas = a->net_for_port (0); - const db::Net *nag = a->net_for_port (1); - const db::Net *nad = a->net_for_port (2); - const db::Net *nab = a->net_for_port (3); - const db::Net *nbs = b->net_for_port (0); - const db::Net *nbg = b->net_for_port (1); - const db::Net *nbd = b->net_for_port (2); - const db::Net *nbb = b->net_for_port (3); + const db::Net *nas = a->net_for_terminal (0); + const db::Net *nag = a->net_for_terminal (1); + const db::Net *nad = a->net_for_terminal (2); + const db::Net *nab = a->net_for_terminal (3); + const db::Net *nbs = b->net_for_terminal (0); + const db::Net *nbg = b->net_for_terminal (1); + const db::Net *nbd = b->net_for_terminal (2); + const db::Net *nbb = b->net_for_terminal (3); // parallel transistors can be combined into one if (((nas == nbs && nad == nbd) || (nas == nbd && nad == nbs)) && nag == nbg && nab == nbb) { @@ -254,10 +254,10 @@ bool DeviceClassMOS4Transistor::combine_devices (Device *a, Device *b) const a->set_parameter_value (1, a->parameter_value (1) + b->parameter_value (1)); a->set_parameter_value (2, a->parameter_value (2) + b->parameter_value (2)); a->set_parameter_value (3, a->parameter_value (3) + b->parameter_value (3)); - b->connect_port (0, 0); - b->connect_port (1, 0); - b->connect_port (2, 0); - b->connect_port (3, 0); + b->connect_terminal (0, 0); + b->connect_terminal (1, 0); + b->connect_terminal (2, 0); + b->connect_terminal (3, 0); return true; diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index a338193f1..949c6b6b0 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -30,9 +30,9 @@ namespace db { /** - * @brief A basic two-port device class + * @brief A basic two-terminal device class */ -class DB_PUBLIC DeviceClassTwoPortDevice +class DB_PUBLIC DeviceClassTwoTerminalDevice : public db::DeviceClass { public: @@ -47,10 +47,10 @@ public: /** * @brief A basic resistor device class * A resistor defines a single parameter, "R", which is the resistance in Ohm. - * It defines two ports, "A" and "B" for the two terminals. + * It defines two terminals, "A" and "B" for the two terminals. */ class DB_PUBLIC DeviceClassResistor - : public db::DeviceClassTwoPortDevice + : public db::DeviceClassTwoTerminalDevice { public: DeviceClassResistor (); @@ -67,10 +67,10 @@ public: /** * @brief A basic capacitor device class * A capacitor defines a single parameter, "C", which is the capacitance in Farad. - * It defines two ports, "A" and "B" for the two terminals. + * It defines two terminals, "A" and "B" for the two terminals. */ class DB_PUBLIC DeviceClassCapacitor - : public db::DeviceClassTwoPortDevice + : public db::DeviceClassTwoTerminalDevice { public: DeviceClassCapacitor (); @@ -87,10 +87,10 @@ public: /** * @brief A basic inductor device class * An inductor defines a single parameter, "L", which is the inductance in Henry. - * It defines two ports, "A" and "B" for the two terminals. + * It defines two terminals, "A" and "B" for the two terminals. */ class DB_PUBLIC DeviceClassInductor - : public db::DeviceClassTwoPortDevice + : public db::DeviceClassTwoTerminalDevice { public: DeviceClassInductor (); @@ -108,7 +108,7 @@ public: * @brief A basic diode device class * A diode defines a single parameter, "A", which is the area in square micrometers (YES: micrometers, as this is the basic unit of measure * in KLayout). - * It defines two ports, "A" and "C" for anode and cathode. + * It defines two terminals, "A" and "C" for anode and cathode. */ class DB_PUBLIC DeviceClassDiode : public db::DeviceClass @@ -129,7 +129,7 @@ public: * @brief A basic MOSFET device class with three terminals * A MOSFET defines four parameters: "W" for the gate width in micrometers, "L" for the gate length in micrometers, * "AS" for the source area and "AD" for the drain area. - * The MOSFET device defines three ports, "S", "D" and "G" for source, drain and gate. + * The MOSFET device defines three terminals, "S", "D" and "G" for source, drain and gate. */ class DB_PUBLIC DeviceClassMOS3Transistor : public db::DeviceClass @@ -149,7 +149,7 @@ public: /** * @brief A basic MOSFET device class with four terminals * The four-terminal MOSFET behaves identical to the three-terminal one but adds one more - * port for the bulk. + * terminal for the bulk. */ class DB_PUBLIC DeviceClassMOS4Transistor : public db::DeviceClass diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 686fa52f3..153bcd0b4 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -62,7 +62,7 @@ void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const mp_layout = &layout; - // port properties are kept in property index 0 + // terminal properties are kept in property index 0 m_propname_id = mp_layout->properties_repository ().prop_name_id (tl::Variant (int (0))); tl_assert (m_netlist.get () != 0); @@ -165,30 +165,30 @@ Device *NetlistDeviceExtractor::create_device (unsigned int device_class_index) return device; } -void NetlistDeviceExtractor::define_port (Device *device, size_t port_id, size_t layer_index, const db::Polygon &polygon) +void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Polygon &polygon) { tl_assert (mp_layout != 0); - // Build a property set for the DevicePortProperty + // Build a property set for the DeviceTerminalProperty db::PropertiesRepository::properties_set ps; tl::Variant &v = ps.insert (std::make_pair (m_propname_id, tl::Variant ()))->second; - v = tl::Variant (new db::DevicePortProperty (db::NetPortRef (device, port_id)), db::NetlistProperty::variant_class (), true); + v = tl::Variant (new db::DeviceTerminalProperty (db::NetTerminalRef (device, terminal_id)), db::NetlistProperty::variant_class (), true); db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); db::PolygonRef pr (polygon, mp_layout->shape_repository ()); mp_layout->cell (m_cell_index).shapes (layer_index).insert (db::PolygonRefWithProperties (pr, pi)); } -void NetlistDeviceExtractor::define_port (Device *device, size_t port_id, size_t layer_index, const db::Box &box) +void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Box &box) { - define_port (device, port_id, layer_index, db::Polygon (box)); + define_terminal (device, terminal_id, layer_index, db::Polygon (box)); } -void NetlistDeviceExtractor::define_port (Device *device, size_t port_id, size_t layer_index, const db::Point &point) +void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Point &point) { // NOTE: we add one DBU to the "point" to prevent it from vanishing db::Vector dv (1, 1); - define_port (device, port_id, layer_index, db::Polygon (db::Box (point - dv, point + dv))); + define_terminal (device, terminal_id, layer_index, db::Polygon (db::Box (point - dv, point + dv))); } } diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index cf585e3e6..fd929ce00 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -71,9 +71,9 @@ public: * cells from the layout. * * Devices will be generated inside the netlist's circuits as they are extracted - * from the layout. Inside the layout, device port annotation shapes are created with the - * corresponding DevicePortProperty objects attached. The will be used when extracting - * the nets later to associate nets with device ports. + * from the layout. Inside the layout, device terminal annotation shapes are created with the + * corresponding DeviceTerminalProperty objects attached. The will be used when extracting + * the nets later to associate nets with device terminals. * * The definition of the input layers is device class specific. * @@ -104,8 +104,8 @@ public: * so the implementation needs to consider this case. The geometries are already merged. * * The implementation of this method shall use "create_device" to create new - * devices based on the geometry found. It shall use "define_port" to define - * ports by which the nets extracted in the network extraction step connect + * devices based on the geometry found. It shall use "define_terminal" to define + * terminals by which the nets extracted in the network extraction step connect * to the new devices. */ virtual void extract_devices (const std::vector &layer_geometry); @@ -126,19 +126,19 @@ protected: Device *create_device (unsigned int device_class_index = 0); /** - * @brief Defines a device port in the layout (a polygon) + * @brief Defines a device terminal in the layout (a polygon) */ - void define_port (Device *device, size_t port_id, size_t layer_index, const db::Polygon &polygon); + void define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Polygon &polygon); /** - * @brief Defines a device port in the layout (a box) + * @brief Defines a device terminal in the layout (a box) */ - void define_port (Device *device, size_t port_id, size_t layer_index, const db::Box &box); + void define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Box &box); /** - * @brief Defines a point-like device port in the layout + * @brief Defines a point-like device terminal in the layout */ - void define_port (Device *device, size_t port_id, size_t layer_index, const db::Point &point); + void define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Point &point); /** * @brief Gets the database unit diff --git a/src/db/db/dbNetlistProperty.cc b/src/db/db/dbNetlistProperty.cc index 27757bede..cf33bfab3 100644 --- a/src/db/db/dbNetlistProperty.cc +++ b/src/db/db/dbNetlistProperty.cc @@ -190,70 +190,70 @@ std::string NetNameProperty::to_string () const } // -------------------------------------------------------------------------------------------- -// DevicePortProperty Implementation +// DeviceTerminalProperty Implementation -DevicePortProperty::DevicePortProperty () +DeviceTerminalProperty::DeviceTerminalProperty () : NetlistProperty () { // .. nothing yet .. } -DevicePortProperty::DevicePortProperty (const DevicePortProperty &other) - : NetlistProperty (other), m_port_ref (other.m_port_ref) +DeviceTerminalProperty::DeviceTerminalProperty (const DeviceTerminalProperty &other) + : NetlistProperty (other), m_terminal_ref (other.m_terminal_ref) { // .. nothing yet .. } -DevicePortProperty::DevicePortProperty (const db::NetPortRef &p) - : NetlistProperty (), m_port_ref (p) +DeviceTerminalProperty::DeviceTerminalProperty (const db::NetTerminalRef &p) + : NetlistProperty (), m_terminal_ref (p) { // .. nothing yet .. } -DevicePortProperty &DevicePortProperty::operator= (const DevicePortProperty &other) +DeviceTerminalProperty &DeviceTerminalProperty::operator= (const DeviceTerminalProperty &other) { NetlistProperty::operator= (other); if (this != &other) { - m_port_ref = other.m_port_ref; + m_terminal_ref = other.m_terminal_ref; } return *this; } -void DevicePortProperty::set_port_ref (const db::NetPortRef &p) +void DeviceTerminalProperty::set_terminal_ref (const db::NetTerminalRef &p) { - m_port_ref = p; + m_terminal_ref = p; } -bool DevicePortProperty::equals (const NetlistProperty *p) const +bool DeviceTerminalProperty::equals (const NetlistProperty *p) const { - const DevicePortProperty *pp = static_cast (p); - return NetlistProperty::equals (p) && m_port_ref == pp->m_port_ref; + const DeviceTerminalProperty *pp = static_cast (p); + return NetlistProperty::equals (p) && m_terminal_ref == pp->m_terminal_ref; } -bool DevicePortProperty::less (const NetlistProperty *p) const +bool DeviceTerminalProperty::less (const NetlistProperty *p) const { if (! NetlistProperty::equals (p)) { return NetlistProperty::less (p); } else { - const DevicePortProperty *pp = static_cast (p); - return m_port_ref < pp->m_port_ref; + const DeviceTerminalProperty *pp = static_cast (p); + return m_terminal_ref < pp->m_terminal_ref; } } -void DevicePortProperty::assign (const NetlistProperty *p) +void DeviceTerminalProperty::assign (const NetlistProperty *p) { NetlistProperty::assign (p); - const DevicePortProperty *pp = static_cast (p); - m_port_ref = pp->m_port_ref; + const DeviceTerminalProperty *pp = static_cast (p); + m_terminal_ref = pp->m_terminal_ref; } -std::string DevicePortProperty::to_string () const +std::string DeviceTerminalProperty::to_string () const { - if (m_port_ref.device () && m_port_ref.port_def ()) { - return "port:" + tl::to_word_or_quoted_string (m_port_ref.device ()->name ()) + ":" + tl::to_word_or_quoted_string (m_port_ref.port_def ()->name ()); + if (m_terminal_ref.device () && m_terminal_ref.terminal_def ()) { + return "terminal:" + tl::to_word_or_quoted_string (m_terminal_ref.device ()->name ()) + ":" + tl::to_word_or_quoted_string (m_terminal_ref.terminal_def ()->name ()); } else { - return "port"; + return "terminal"; } } diff --git a/src/db/db/dbNetlistProperty.h b/src/db/db/dbNetlistProperty.h index 8e8e6bd47..c49ec0a03 100644 --- a/src/db/db/dbNetlistProperty.h +++ b/src/db/db/dbNetlistProperty.h @@ -224,56 +224,56 @@ private: }; /** - * @brief A reference to a device port + * @brief A reference to a device terminal * - * This property is used to mark a shape as a device port reference. - * Such a port reference points to a port of a specific device. + * This property is used to mark a shape as a device terminal reference. + * Such a terminal reference points to a terminal of a specific device. * Attaching such a property to a shape allows connecting the * net to the device later. */ -class DB_PUBLIC DevicePortProperty +class DB_PUBLIC DeviceTerminalProperty : public db::NetlistProperty { public: /** * @brief Creates a netlist name property without a specific name */ - DevicePortProperty (); + DeviceTerminalProperty (); /** * @brief copy constructor */ - DevicePortProperty (const db::DevicePortProperty &other); + DeviceTerminalProperty (const db::DeviceTerminalProperty &other); /** * @brief Creates a netlist name property with the given name */ - DevicePortProperty (const db::NetPortRef &port_ref); + DeviceTerminalProperty (const db::NetTerminalRef &terminal_ref); /** * @brief Assignment */ - DevicePortProperty &operator= (const DevicePortProperty &other); + DeviceTerminalProperty &operator= (const DeviceTerminalProperty &other); /** - * @brief Sets the port reference + * @brief Sets the terminal reference */ - void set_port_ref (const db::NetPortRef &port_ref); + void set_terminal_ref (const db::NetTerminalRef &terminal_ref); /** - * @brief Gets the port reference + * @brief Gets the terminal reference */ - const db::NetPortRef &port_ref () const + const db::NetTerminalRef &terminal_ref () const { - return m_port_ref; + return m_terminal_ref; } /** * @brief Clones the object */ - virtual DevicePortProperty *clone () const + virtual DeviceTerminalProperty *clone () const { - return new DevicePortProperty (*this); + return new DeviceTerminalProperty (*this); } /** @@ -297,7 +297,7 @@ public: virtual std::string to_string () const; private: - db::NetPortRef m_port_ref; + db::NetTerminalRef m_terminal_ref; }; } diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index a9abc0c41..8a12b5c14 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -42,9 +42,9 @@ Class decl_dbPin ("db", "Pin", "This class has been added in version 0.26." ); -static void device_disconnect_port (db::Device *device, size_t port_id) +static void device_disconnect_terminal (db::Device *device, size_t terminal_id) { - device->connect_port (port_id, 0); + device->connect_terminal (terminal_id, 0); } static bool device_has_param_with_name (const db::DeviceClass *device_class, const std::string &name) @@ -93,15 +93,15 @@ Class decl_dbDevice ("db", "Device", gsi::method ("name", &db::Device::name, "@brief Gets the name of the device.\n" ) + - gsi::method ("net_for_port", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_port, gsi::arg ("port_id"), - "@brief Gets the net connected to the specified port.\n" - "If the port is not connected, nil is returned for the net." + gsi::method ("net_for_terminal", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the net connected to the specified terminal.\n" + "If the terminal is not connected, nil is returned for the net." ) + - gsi::method ("connect_port", &db::Device::connect_port, gsi::arg ("port_id"), gsi::arg ("net"), - "@brief Connects the given port to the specified net.\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" ) + - gsi::method_ext ("disconnect_port", &device_disconnect_port, gsi::arg ("port_id"), - "@brief Disconnects the given port from any net.\n" + gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal, gsi::arg ("terminal_id"), + "@brief Disconnects the given terminal from any net.\n" ) + gsi::method ("parameter", &db::Device::parameter_value, gsi::arg ("param_id"), "@brief Gets the parameter value for the given parameter ID." @@ -123,12 +123,12 @@ Class decl_dbDevice ("db", "Device", "The type of device is represented by a \\DeviceClass object. Device objects " "live in \\Circuit objects, the device class objects live in the \\Netlist object.\n" "\n" - "Devices connect to nets through ports. Ports are described by a port ID which is " - "essentially the zero-based index of the port. Port definitions can be " - "obtained from the device class using the \\DeviceClass#port_definitions method.\n" + "Devices connect to nets through terminals. Terminals are described by a terminal ID which is " + "essentially the zero-based index of the terminal. Terminal definitions can be " + "obtained from the device class using the \\DeviceClass#terminal_definitions method.\n" "\n" - "Devices connect to nets through the \\Device#connect_port method. " - "Device ports can be disconnected using \\Device#disconnect_port.\n" + "Devices connect to nets through the \\Device#connect_terminal method. " + "Device terminals can be disconnected using \\Device#disconnect_terminal.\n" "\n" "This class has been added in version 0.26." ); @@ -195,24 +195,24 @@ Class decl_dbSubCircuit ("db", "SubCircuit", "This class has been added in version 0.26." ); -Class decl_dbNetPortRef ("db", "NetPortRef", - gsi::method ("port_id", &db::NetPortRef::port_id, - "@brief Gets the ID of the port of the device the connection is made to." +Class decl_dbNetTerminalRef ("db", "NetTerminalRef", + gsi::method ("terminal_id", &db::NetTerminalRef::terminal_id, + "@brief Gets the ID of the terminal of the device the connection is made to." ) + - gsi::method ("device", (db::Device *(db::NetPortRef::*) ()) &db::NetPortRef::device, + gsi::method ("device", (db::Device *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::device, "@brief Gets the device reference.\n" "Gets the device object that this connection is made to." ) + - gsi::method ("net", (db::Net *(db::NetPortRef::*) ()) &db::NetPortRef::net, - "@brief Gets the net this port reference is attached to" + gsi::method ("net", (db::Net *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::net, + "@brief Gets the net this terminal reference is attached to" ) + - gsi::method ("device_class", (db::DeviceClass *(db::NetPortRef::*) ()) &db::NetPortRef::device_class, + gsi::method ("device_class", (db::DeviceClass *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::device_class, "@brief Gets the class of the device which is addressed." ) + - gsi::method ("port_def", (db::DevicePortDefinition *(db::NetPortRef::*) ()) &db::NetPortRef::port_def, - "@brief Gets the port definition of the port that is connected" + gsi::method ("terminal_def", (db::DeviceTerminalDefinition *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::terminal_def, + "@brief Gets the terminal definition of the terminal that is connected" ), - "@brief A connection to a port of a device.\n" + "@brief A connection to a terminal of a device.\n" "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" "\n" "This class has been added in version 0.26." @@ -273,54 +273,54 @@ Class decl_dbNet ("db", "Net", "are either connections to subcircuit pins or to outgoing pins of the " "circuit the net lives in." ) + - gsi::iterator ("each_port", (db::Net::port_iterator (db::Net::*) ()) &db::Net::begin_ports, (db::Net::port_iterator (db::Net::*) ()) &db::Net::end_ports, - "@brief Iterates over all ports the net connects.\n" - "Ports connect devices. Port connections are described by \\NetPortRef " + gsi::iterator ("each_terminal", (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::begin_terminals, (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::end_terminals, + "@brief Iterates over all terminals the net connects.\n" + "Terminals connect devices. Terminal connections are described by \\NetTerminalRef " "objects." ), "@brief A single net.\n" - "A net connects multiple pins or ports together. Pins are either " + "A net connects multiple pins or terminals together. Pins are either " "pin or subcircuits of outgoing pins of the circuit the net lives in. " - "Ports are connections made to specific ports of devices.\n" + "Terminals are connections made to specific terminals of devices.\n" "\n" "To connect a net to an outgoing pin of a circuit, use \\Circuit#connect_pin, to " "disconnect a net from an outgoing pin use \\Circuit#disconnect_pin. " "To connect a net to a pin of a subcircuit, use \\SubCircuit#connect_pin, to " "disconnect a net from a pin of a subcircuit, use \\SubCircuit#disconnect_pin. " - "To connect a net to a port of a device, use \\Device#connect_port, to " - "disconnect a net from a port of a device, use \\Device#disconnect_port.\n" + "To connect a net to a terminal of a device, use \\Device#connect_terminal, to " + "disconnect a net from a terminal of a device, use \\Device#disconnect_terminal.\n" "\n" "This class has been added in version 0.26." ); -static db::DevicePortDefinition *new_port_definition (const std::string &name, const std::string &description) +static db::DeviceTerminalDefinition *new_terminal_definition (const std::string &name, const std::string &description) { - return new db::DevicePortDefinition (name, description); + return new db::DeviceTerminalDefinition (name, description); } -Class decl_dbDevicePortDefinition ("db", "DevicePortDefinition", - gsi::constructor ("new", &gsi::new_port_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), - "@brief Creates a new port definition." +Class decl_dbDeviceTerminalDefinition ("db", "DeviceTerminalDefinition", + gsi::constructor ("new", &gsi::new_terminal_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), + "@brief Creates a new terminal definition." ) + - gsi::method ("name", &db::DevicePortDefinition::name, - "@brief Gets the name of the port." + gsi::method ("name", &db::DeviceTerminalDefinition::name, + "@brief Gets the name of the terminal." ) + - gsi::method ("name=", &db::DevicePortDefinition::set_name, gsi::arg ("name"), - "@brief Sets the name of the port." + gsi::method ("name=", &db::DeviceTerminalDefinition::set_name, gsi::arg ("name"), + "@brief Sets the name of the terminal." ) + - gsi::method ("description", &db::DevicePortDefinition::description, - "@brief Gets the description of the port." + gsi::method ("description", &db::DeviceTerminalDefinition::description, + "@brief Gets the description of the terminal." ) + - gsi::method ("description=", &db::DevicePortDefinition::set_description, gsi::arg ("description"), - "@brief Sets the description of the port." + gsi::method ("description=", &db::DeviceTerminalDefinition::set_description, gsi::arg ("description"), + "@brief Sets the description of the terminal." ) + - gsi::method ("id", &db::DevicePortDefinition::id, - "@brief Gets the ID of the port.\n" - "The ID of the port is used in some places to refer to a specific port (e.g. in " - "the \\NetPortRef object)." + gsi::method ("id", &db::DeviceTerminalDefinition::id, + "@brief Gets the ID of the terminal.\n" + "The ID of the terminal is used in some places to refer to a specific terminal (e.g. in " + "the \\NetTerminalRef object)." ), - "@brief A port descriptor\n" - "This class is used inside the \\DeviceClass class to describe a port of the device.\n" + "@brief A terminal descriptor\n" + "This class is used inside the \\DeviceClass class to describe a terminal of the device.\n" "\n" "This class has been added in version 0.26." ); @@ -385,13 +385,13 @@ Class decl_dbDeviceClass ("db", "DeviceClass", "to check for object identity - i.e. to determine whether two devices share the " "same device class." ) + - gsi::method ("port_definitions", &db::DeviceClass::port_definitions, - "@brief Gets the list of port definitions of the device.\n" - "See the \\DevicePortDefinition class description for details." + gsi::method ("terminal_definitions", &db::DeviceClass::terminal_definitions, + "@brief Gets the list of terminal definitions of the device.\n" + "See the \\DeviceTerminalDefinition class description for details." ) + - gsi::method ("port_definition", &db::DeviceClass::port_definition, gsi::arg ("port_id"), - "@brief Gets the port definition object for a given ID.\n" - "Port definition IDs are used in some places to reference a specific port of a device. " + gsi::method ("terminal_definition", &db::DeviceClass::terminal_definition, gsi::arg ("terminal_id"), + "@brief Gets the terminal definition object for a given ID.\n" + "Terminal definition IDs are used in some places to reference a specific terminal of a device. " "This method obtains the corresponding definition object." ) + gsi::method ("parameter_definitions", &db::DeviceClass::parameter_definitions, @@ -455,10 +455,10 @@ private: } -static void gdc_add_port_definition (GenericDeviceClass *cls, db::DevicePortDefinition *port_def) +static void gdc_add_terminal_definition (GenericDeviceClass *cls, db::DeviceTerminalDefinition *terminal_def) { - if (port_def) { - *port_def = cls->add_port_definition (*port_def); + if (terminal_def) { + *terminal_def = cls->add_terminal_definition (*terminal_def); } } @@ -470,17 +470,17 @@ static void gdc_add_parameter_definition (GenericDeviceClass *cls, db::DevicePar } Class decl_GenericDeviceClass (decl_dbDeviceClass, "db", "GenericDeviceClass", - gsi::method_ext ("add_port", &gsi::gdc_add_port_definition, gsi::arg ("port_def"), - "@brief Adds the given port definition to the device class\n" - "This method will define a new port. The new port is added at the end of existing ports. " - "The port definition object passed as the argument is modified to contain the " - "new ID of the port.\n" + gsi::method_ext ("add_terminal", &gsi::gdc_add_terminal_definition, gsi::arg ("terminal_def"), + "@brief Adds the given terminal definition to the device class\n" + "This method will define a new terminal. The new terminal is added at the end of existing terminals. " + "The terminal definition object passed as the argument is modified to contain the " + "new ID of the terminal.\n" "\n" - "The port is copied into the device class. Modifying the port object later " - "does not have the effect of changing the port definition." + "The terminal is copied into the device class. Modifying the terminal object later " + "does not have the effect of changing the terminal definition." ) + - gsi::method ("clear_ports", &GenericDeviceClass::clear_port_definitions, - "@brief Clears the list of ports\n" + gsi::method ("clear_terminals", &GenericDeviceClass::clear_terminal_definitions, + "@brief Clears the list of terminals\n" ) + gsi::method_ext ("add_parameter", &gsi::gdc_add_parameter_definition, gsi::arg ("parameter_def"), "@brief Adds the given parameter definition to the device class\n" @@ -501,10 +501,10 @@ Class decl_GenericDeviceClass (decl_dbDeviceClass, "db", "Ge "@brief Sets the description of the device\n" ), "@brief A generic device class\n" - "This class allows building generic device classes. Specificially, ports can be defined " - "by adding port definitions. Port definitions should not be added dynamically. To create " + "This class allows building generic device classes. Specificially, terminals can be defined " + "by adding terminal definitions. Terminal definitions should not be added dynamically. To create " "your own device, instantiate the \\GenericDeviceClass object, set name and description and " - "specify the ports. Then add this new device class to the \\Netlist object where it will live " + "specify the terminals. Then add this new device class to the \\Netlist object where it will live " "and be used to define device instances (\\Device objects).\n" "\n" "In addition, parameters can be defined which correspond to values stored inside the " @@ -695,11 +695,11 @@ Class decl_dbCircuit ("db", "Circuit", "Devices are created using the \\create_device method. Subcircuits are " "created using the \\create_subcircuit method.\n" "\n" - "Devices are connected through 'ports', subcircuits are connected through " - "their pins. Ports and pins are described by integer ID's in the context of " + "Devices are connected through 'terminals', subcircuits are connected through " + "their pins. Terminals and pins are described by integer ID's in the context of " "most methods.\n" "\n" - "Finally, the circuit consists of the nets. Nets connect ports of devices " + "Finally, the circuit consists of the nets. Nets connect terminals of devices " "and pins of subcircuits or the circuit itself. Nets are created using " "\\create_net and are represented by objects of the \\Net class.\n" "See there for more about nets.\n" diff --git a/src/db/db/gsiDeclDbNetlistProperty.cc b/src/db/db/gsiDeclDbNetlistProperty.cc index b4a2fa80c..8d504f6c0 100644 --- a/src/db/db/gsiDeclDbNetlistProperty.cc +++ b/src/db/db/gsiDeclDbNetlistProperty.cc @@ -46,7 +46,7 @@ gsi::Class decl_NetlistProperty ("db", "NetlistProperty", "@brief A generic base class for netlist properties.\n" "\n" "Netlist properties are used to annotate shapes or other objects with net properties. " - "Netlist properties are net names or device ports. " + "Netlist properties are net names or device terminals. " "Netlist properties can be stored inside property sets. " "This class provides the base class for such netlist properties." "\n\n" @@ -87,34 +87,34 @@ gsi::Class decl_NetNameProperty (decl_NetlistProperty, "db" ); // --------------------------------------------------------------- -// db::DevicePortProperty binding +// db::DeviceTerminalProperty binding -static db::DevicePortProperty *new_devport () +static db::DeviceTerminalProperty *new_devterminal () { - return new db::DevicePortProperty (); + return new db::DeviceTerminalProperty (); } -static db::DevicePortProperty *new_devport2 (const db::NetPortRef &n) +static db::DeviceTerminalProperty *new_devterminal2 (const db::NetTerminalRef &n) { - return new db::DevicePortProperty (n); + return new db::DeviceTerminalProperty (n); } -gsi::Class decl_DevicePortProperty (decl_NetlistProperty, "db", "DevicePortProperty", - gsi::constructor ("new", &new_devport, - "@brief Creates a new device port property" +gsi::Class decl_DeviceTerminalProperty (decl_NetlistProperty, "db", "DeviceTerminalProperty", + gsi::constructor ("new", &new_devterminal, + "@brief Creates a new device terminal property" ) + - gsi::constructor ("new", &new_devport2, gsi::arg ("port_ref"), - "@brief Creates a new device port property with the given port reference" + gsi::constructor ("new", &new_devterminal2, gsi::arg ("terminal_ref"), + "@brief Creates a new device terminal property with the given terminal reference" ) + - gsi::method ("port_ref=", &db::DevicePortProperty::set_port_ref, gsi::arg ("p"), - "@brief Sets the port reference\n" + gsi::method ("terminal_ref=", &db::DeviceTerminalProperty::set_terminal_ref, gsi::arg ("p"), + "@brief Sets the terminal reference\n" ) + - gsi::method ("port_ref", &db::DevicePortProperty::port_ref, - "@brief Gets the port reference\n" + gsi::method ("terminal_ref", &db::DeviceTerminalProperty::terminal_ref, + "@brief Gets the terminal reference\n" ), - "@brief A device port reference property.\n" + "@brief A device terminal reference property.\n" "\n" - "The netlist property annotates a shape or other object with a reference to a device port." + "The netlist property annotates a shape or other object with a reference to a device terminal." "\n\n" "This class was introduced in version 0.26.\n" ); diff --git a/src/db/unit_tests/dbNetlistPropertyTests.cc b/src/db/unit_tests/dbNetlistPropertyTests.cc index e415664d4..420ebf1c9 100644 --- a/src/db/unit_tests/dbNetlistPropertyTests.cc +++ b/src/db/unit_tests/dbNetlistPropertyTests.cc @@ -47,23 +47,23 @@ TEST(1_NameBasic) EXPECT_EQ (n2.to_string (), "name:'\"quoted\"'"); } -TEST(2_PortRefBasic) +TEST(2_TerminalRefBasic) { db::DeviceClass dc; - dc.add_port_definition (db::DevicePortDefinition ("A", "Port A")); - dc.add_port_definition (db::DevicePortDefinition ("B", "Port B")); + dc.add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); + dc.add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B")); db::Device d (&dc, "D"); - db::DevicePortProperty dp (db::NetPortRef (&d, 1)); - EXPECT_EQ (dp.to_string (), "port:D:B"); + db::DeviceTerminalProperty dp (db::NetTerminalRef (&d, 1)); + EXPECT_EQ (dp.to_string (), "terminal:D:B"); - dp.set_port_ref (db::NetPortRef (&d, 0)); - EXPECT_EQ (dp.to_string (), "port:D:A"); - EXPECT_EQ (dp.port_ref () == db::NetPortRef (&d, 0), true); + dp.set_terminal_ref (db::NetTerminalRef (&d, 0)); + EXPECT_EQ (dp.to_string (), "terminal:D:A"); + EXPECT_EQ (dp.terminal_ref () == db::NetTerminalRef (&d, 0), true); - db::DevicePortProperty dp2 = dp; - EXPECT_EQ (dp2.to_string (), "port:D:A"); + db::DeviceTerminalProperty dp2 = dp; + EXPECT_EQ (dp2.to_string (), "terminal:D:A"); } TEST(3_Variants) diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 423c56351..881c5aa61 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -28,7 +28,7 @@ #include -static std::string pd2string (const db::DevicePortDefinition &pd) +static std::string pd2string (const db::DeviceTerminalDefinition &pd) { return pd.name () + "(" + pd.description () + ") #" + tl::to_string (pd.id ()); } @@ -38,16 +38,16 @@ static std::string pd2string (const db::DeviceParameterDefinition &pd) return pd.name () + "(" + pd.description () + ")=" + tl::to_string (pd.default_value ()) + " #" + tl::to_string (pd.id ()); } -TEST(1_DevicePortDefinition) +TEST(1_DeviceTerminalDefinition) { - db::DevicePortDefinition pd; + db::DeviceTerminalDefinition pd; EXPECT_EQ (pd2string (pd), "() #0"); pd.set_name ("name"); pd.set_description ("nothing yet"); EXPECT_EQ (pd2string (pd), "name(nothing yet) #0"); - db::DevicePortDefinition pd2; + db::DeviceTerminalDefinition pd2; pd2 = pd; EXPECT_EQ (pd2string (pd2), "name(nothing yet) #0"); pd2.set_name ("name2"); @@ -55,13 +55,13 @@ TEST(1_DevicePortDefinition) EXPECT_EQ (pd2string (pd2), "name2(now it has something) #0"); db::DeviceClass dc; - dc.add_port_definition (pd); - dc.add_port_definition (pd2); - EXPECT_EQ (pd2string (dc.port_definitions ()[0]), "name(nothing yet) #0"); - EXPECT_EQ (pd2string (dc.port_definitions ()[1]), "name2(now it has something) #1"); + dc.add_terminal_definition (pd); + dc.add_terminal_definition (pd2); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[0]), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[1]), "name2(now it has something) #1"); - dc.clear_port_definitions (); - EXPECT_EQ (dc.port_definitions ().empty (), true); + dc.clear_terminal_definitions (); + EXPECT_EQ (dc.terminal_definitions ().empty (), true); db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0); dc.add_parameter_definition (ppd); @@ -79,11 +79,11 @@ TEST(1_DevicePortDefinition) TEST(2_DeviceClass) { - db::DevicePortDefinition pd; + db::DeviceTerminalDefinition pd; pd.set_name ("name"); pd.set_description ("nothing yet"); - db::DevicePortDefinition pd2; + db::DeviceTerminalDefinition pd2; pd2.set_name ("name2"); pd2.set_description ("now it has something"); @@ -92,23 +92,23 @@ TEST(2_DeviceClass) dc.set_description ("devdesc"); EXPECT_EQ (dc.name (), "devname"); EXPECT_EQ (dc.description (), "devdesc"); - dc.add_port_definition (pd); - dc.add_port_definition (pd2); - EXPECT_EQ (dc.port_definitions ().size (), size_t (2)); - EXPECT_EQ (pd2string (dc.port_definitions ()[0]), "name(nothing yet) #0"); - EXPECT_EQ (pd2string (dc.port_definitions ()[1]), "name2(now it has something) #1"); + dc.add_terminal_definition (pd); + dc.add_terminal_definition (pd2); + EXPECT_EQ (dc.terminal_definitions ().size (), size_t (2)); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[0]), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[1]), "name2(now it has something) #1"); - EXPECT_EQ (pd2string (*dc.port_definition (dc.port_definitions ()[0].id ())), "name(nothing yet) #0"); - EXPECT_EQ (pd2string (*dc.port_definition (dc.port_definitions ()[1].id ())), "name2(now it has something) #1"); - EXPECT_EQ (dc.port_definition (3), 0); + EXPECT_EQ (pd2string (*dc.terminal_definition (dc.terminal_definitions ()[0].id ())), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (*dc.terminal_definition (dc.terminal_definitions ()[1].id ())), "name2(now it has something) #1"); + EXPECT_EQ (dc.terminal_definition (3), 0); db::DeviceClass dc2 = dc; EXPECT_EQ (dc2.name (), "devname"); EXPECT_EQ (dc2.description (), "devdesc"); - EXPECT_EQ (dc2.port_definitions ().size (), size_t (2)); - EXPECT_EQ (pd2string (*dc2.port_definition (dc2.port_definitions ()[0].id ())), "name(nothing yet) #0"); - EXPECT_EQ (pd2string (*dc2.port_definition (dc2.port_definitions ()[1].id ())), "name2(now it has something) #1"); - EXPECT_EQ (dc2.port_definition (3), 0); + EXPECT_EQ (dc2.terminal_definitions ().size (), size_t (2)); + EXPECT_EQ (pd2string (*dc2.terminal_definition (dc2.terminal_definitions ()[0].id ())), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (*dc2.terminal_definition (dc2.terminal_definitions ()[1].id ())), "name2(now it has something) #1"); + EXPECT_EQ (dc2.terminal_definition (3), 0); } static std::string pins2string (const db::Circuit &c) @@ -152,13 +152,13 @@ TEST(3_CircuitBasic) static std::string net2string (const db::Net &n) { std::string res; - for (db::Net::const_port_iterator i = n.begin_ports (); i != n.end_ports (); ++i) { + for (db::Net::const_terminal_iterator i = n.begin_terminals (); i != n.end_terminals (); ++i) { if (! res.empty ()) { res += ","; } res += i->device () ? i->device ()->name () : "(null)"; res += ":"; - res += i->port_def () ? i->port_def ()->name () : "(null)"; + res += i->terminal_def () ? i->terminal_def ()->name () : "(null)"; } for (db::Net::const_pin_iterator i = n.begin_pins (); i != n.end_pins (); ++i) { if (! res.empty ()) { @@ -208,12 +208,12 @@ static std::string netlist2 (const db::Circuit &c) continue; } pins.clear (); - for (size_t i = 0; i < d->device_class ()->port_definitions ().size (); ++i) { + for (size_t i = 0; i < d->device_class ()->terminal_definitions ().size (); ++i) { if (! pins.empty ()) { pins += ","; } - const db::Net *net = d->net_for_port (i); - pins += d->device_class ()->port_definitions () [i].name (); + const db::Net *net = d->net_for_terminal (i); + pins += d->device_class ()->terminal_definitions () [i].name (); pins += "="; pins += net ? net->name () : std::string ("(null)"); } @@ -244,16 +244,16 @@ TEST(4_CircuitDevices) { db::DeviceClass dc1; dc1.set_name ("dc1"); - dc1.add_port_definition (db::DevicePortDefinition ("S", "Source")); - dc1.add_port_definition (db::DevicePortDefinition ("G", "Gate")); - dc1.add_port_definition (db::DevicePortDefinition ("D", "Drain")); + dc1.add_terminal_definition (db::DeviceTerminalDefinition ("S", "Source")); + dc1.add_terminal_definition (db::DeviceTerminalDefinition ("G", "Gate")); + dc1.add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain")); dc1.add_parameter_definition (db::DeviceParameterDefinition ("U", "", 1.0)); dc1.add_parameter_definition (db::DeviceParameterDefinition ("V", "", 2.0)); db::DeviceClass dc2; dc2.set_name ("dc2"); - dc2.add_port_definition (db::DevicePortDefinition ("A", "")); - dc2.add_port_definition (db::DevicePortDefinition ("B", "")); + dc2.add_terminal_definition (db::DeviceTerminalDefinition ("A", "")); + dc2.add_terminal_definition (db::DeviceTerminalDefinition ("B", "")); dc2.add_parameter_definition (db::DeviceParameterDefinition ("U", "", 2.0)); dc2.add_parameter_definition (db::DeviceParameterDefinition ("V", "", 1.0)); @@ -297,16 +297,16 @@ TEST(4_CircuitDevices) n1->set_name ("n1"); EXPECT_EQ (n1->circuit (), 0); c->add_net (n1); - n1->add_port (db::NetPortRef (d1, 0)); - n1->add_port (db::NetPortRef (d2a, 0)); + n1->add_terminal (db::NetTerminalRef (d1, 0)); + n1->add_terminal (db::NetTerminalRef (d2a, 0)); EXPECT_EQ (n1->circuit (), c.get ()); db::Net *n2 = new db::Net (); n2->set_name ("n2"); c->add_net (n2); - n2->add_port (db::NetPortRef (d1, 1)); - n2->add_port (db::NetPortRef (d2a, 1)); - n2->add_port (db::NetPortRef (d2b, 0)); + n2->add_terminal (db::NetTerminalRef (d1, 1)); + n2->add_terminal (db::NetTerminalRef (d2a, 1)); + n2->add_terminal (db::NetTerminalRef (d2b, 0)); EXPECT_EQ (netlist2 (*c), "c:\n" @@ -318,8 +318,8 @@ TEST(4_CircuitDevices) db::Net *n3 = new db::Net (); n3->set_name ("n3"); c->add_net (n3); - n3->add_port (db::NetPortRef (d1, 2)); - n3->add_port (db::NetPortRef (d2b, 1)); + n3->add_terminal (db::NetTerminalRef (d1, 2)); + n3->add_terminal (db::NetTerminalRef (d2b, 1)); EXPECT_EQ (nets2string (*c), "d1:S,d2a:A\n" @@ -378,8 +378,8 @@ TEST(4_NetlistSubcircuits) db::DeviceClass *dc = new db::DeviceClass (); dc->set_name ("dc2"); - dc->add_port_definition (db::DevicePortDefinition ("A", "")); - dc->add_port_definition (db::DevicePortDefinition ("B", "")); + dc->add_terminal_definition (db::DeviceTerminalDefinition ("A", "")); + dc->add_terminal_definition (db::DeviceTerminalDefinition ("B", "")); nl->add_device_class (dc); db::Circuit *c1 = new db::Circuit (); @@ -411,12 +411,12 @@ TEST(4_NetlistSubcircuits) c2->add_net (n2a); n2a->set_name ("n2a"); n2a->add_pin (db::NetPinRef (0)); - n2a->add_port (db::NetPortRef (d, 0)); + n2a->add_terminal (db::NetTerminalRef (d, 0)); db::Net *n2b = new db::Net (); c2->add_net (n2b); n2b->set_name ("n2b"); - n2b->add_port (db::NetPortRef (d, 1)); + n2b->add_terminal (db::NetTerminalRef (d, 1)); n2b->add_pin (db::NetPinRef (1)); db::Net *n1a = new db::Net (); @@ -458,7 +458,7 @@ TEST(4_NetlistSubcircuits) // check netlist for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { for (db::Circuit::net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - for (db::Net::port_iterator i = n->begin_ports (); i != n->end_ports (); ++i) { + for (db::Net::terminal_iterator i = n->begin_terminals (); i != n->end_terminals (); ++i) { EXPECT_EQ (i->net (), n.operator-> ()); } for (db::Net::pin_iterator i = n->begin_pins (); i != n->end_pins (); ++i) { @@ -493,7 +493,7 @@ TEST(4_NetlistSubcircuits) // check netlist for (db::Netlist::circuit_iterator c = nl2.begin_circuits (); c != nl2.end_circuits (); ++c) { for (db::Circuit::net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - for (db::Net::port_iterator i = n->begin_ports (); i != n->end_ports (); ++i) { + for (db::Net::terminal_iterator i = n->begin_terminals (); i != n->end_terminals (); ++i) { EXPECT_EQ (i->net (), n.operator-> ()); } for (db::Net::pin_iterator i = n->begin_pins (); i != n->end_pins (); ++i) { @@ -535,12 +535,12 @@ TEST(6_Net) EXPECT_EQ (int (n.cluster_id ()), 0); } -TEST(7_NetPortsEditing) +TEST(7_NetTerminalsEditing) { db::Circuit c; db::DeviceClass dc; - dc.add_port_definition (db::DevicePortDefinition ("A", "")); - dc.add_port_definition (db::DevicePortDefinition ("B", "")); + dc.add_terminal_definition (db::DeviceTerminalDefinition ("A", "")); + dc.add_terminal_definition (db::DeviceTerminalDefinition ("B", "")); db::Device *d1 = new db::Device (&dc, "D1"); c.add_device (d1); @@ -555,31 +555,31 @@ TEST(7_NetPortsEditing) n2->set_name ("n2"); c.add_net (n2); - d1->connect_port (0, n1); - d1->connect_port (1, n2); + d1->connect_terminal (0, n1); + d1->connect_terminal (1, n2); - d2->connect_port (1, n1); - d2->connect_port (0, n2); + d2->connect_terminal (1, n1); + d2->connect_terminal (0, n2); - EXPECT_EQ (d1->net_for_port (0), n1); - EXPECT_EQ (d1->net_for_port (1), n2); - EXPECT_EQ (d2->net_for_port (0), n2); - EXPECT_EQ (d2->net_for_port (1), n1); + EXPECT_EQ (d1->net_for_terminal (0), n1); + EXPECT_EQ (d1->net_for_terminal (1), n2); + EXPECT_EQ (d2->net_for_terminal (0), n2); + EXPECT_EQ (d2->net_for_terminal (1), n1); EXPECT_EQ (net2string (*n1), "D1:A,D2:B"); EXPECT_EQ (net2string (*n2), "D1:B,D2:A"); - d1->connect_port (0, n2); - d1->connect_port (1, n1); + d1->connect_terminal (0, n2); + d1->connect_terminal (1, n1); - EXPECT_EQ (d1->net_for_port (0), n2); - EXPECT_EQ (d1->net_for_port (1), n1); + EXPECT_EQ (d1->net_for_terminal (0), n2); + EXPECT_EQ (d1->net_for_terminal (1), n1); EXPECT_EQ (net2string (*n1), "D2:B,D1:B"); EXPECT_EQ (net2string (*n2), "D2:A,D1:A"); - d1->connect_port (0, 0); - EXPECT_EQ (d1->net_for_port (0), 0); + d1->connect_terminal (0, 0); + EXPECT_EQ (d1->net_for_terminal (0), 0); EXPECT_EQ (net2string (*n1), "D2:B,D1:B"); EXPECT_EQ (net2string (*n2), "D2:A"); @@ -601,8 +601,8 @@ TEST(7_NetPortsEditing) EXPECT_EQ (net2string (*n2), "D2:A"); - EXPECT_EQ (d2->net_for_port (0), n2); - EXPECT_EQ (d2->net_for_port (1), 0); + EXPECT_EQ (d2->net_for_terminal (0), n2); + EXPECT_EQ (d2->net_for_terminal (1), 0); } TEST(8_NetSubCircuitsEditing) @@ -702,18 +702,18 @@ TEST(8_NetSubCircuitsEditing) EXPECT_EQ (sc2->net_for_pin (1), 0); } -TEST(9_NetPortRefBasics) +TEST(9_NetTerminalRefBasics) { db::Device d1, d2; - EXPECT_EQ (db::NetPortRef (&d1, 0) == db::NetPortRef (&d1, 0), true); - EXPECT_EQ (db::NetPortRef (&d1, 0) == db::NetPortRef (&d1, 1), false); - EXPECT_EQ (db::NetPortRef (&d1, 0) == db::NetPortRef (&d2, 0), false); + EXPECT_EQ (db::NetTerminalRef (&d1, 0) == db::NetTerminalRef (&d1, 0), true); + EXPECT_EQ (db::NetTerminalRef (&d1, 0) == db::NetTerminalRef (&d1, 1), false); + EXPECT_EQ (db::NetTerminalRef (&d1, 0) == db::NetTerminalRef (&d2, 0), false); - EXPECT_EQ (db::NetPortRef (&d1, 0) < db::NetPortRef (&d1, 0), false); - EXPECT_EQ (db::NetPortRef (&d1, 0) < db::NetPortRef (&d1, 1), true); - EXPECT_EQ (db::NetPortRef (&d1, 1) < db::NetPortRef (&d1, 0), false); - EXPECT_NE ((db::NetPortRef (&d1, 0) < db::NetPortRef (&d2, 0)), (db::NetPortRef (&d2, 0) < db::NetPortRef (&d1, 0))); + EXPECT_EQ (db::NetTerminalRef (&d1, 0) < db::NetTerminalRef (&d1, 0), false); + EXPECT_EQ (db::NetTerminalRef (&d1, 0) < db::NetTerminalRef (&d1, 1), true); + EXPECT_EQ (db::NetTerminalRef (&d1, 1) < db::NetTerminalRef (&d1, 0), false); + EXPECT_NE ((db::NetTerminalRef (&d1, 0) < db::NetTerminalRef (&d2, 0)), (db::NetTerminalRef (&d2, 0) < db::NetTerminalRef (&d1, 0))); } TEST(10_NetPinRefBasics) diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index a5fdecad4..08fe2d127 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -141,15 +141,15 @@ class DBNetlist_TestClass < TestBase dc.name = "DC" dc.description = "A device class" - pd = RBA::DevicePortDefinition::new + pd = RBA::DeviceTerminalDefinition::new pd.name = "A" - pd.description = "Port A" - dc.add_port(pd) + pd.description = "Terminal A" + dc.add_terminal(pd) - pd = RBA::DevicePortDefinition::new + pd = RBA::DeviceTerminalDefinition::new pd.name = "B" - pd.description = "Port B" - dc.add_port(pd) + pd.description = "Terminal B" + dc.add_terminal(pd) c = RBA::Circuit::new nl.add(c) @@ -185,31 +185,31 @@ class DBNetlist_TestClass < TestBase net = c.create_net("NET") - d1.connect_port(1, net) - assert_equal(d1.net_for_port(1).name, "NET") - assert_equal(d1.net_for_port(0).inspect, "nil") + d1.connect_terminal(1, net) + assert_equal(d1.net_for_terminal(1).name, "NET") + assert_equal(d1.net_for_terminal(0).inspect, "nil") - d2.connect_port(0, net) + d2.connect_terminal(0, net) dnames = [] - net.each_port { |p| dnames << p.device.name + ":" + p.port_def.name } + net.each_terminal { |p| dnames << p.device.name + ":" + p.terminal_def.name } assert_equal(dnames, [ "D1:B", "D2:A" ]) dnames = [] - net.each_port { |p| dnames << p.device_class.name + ":" + p.port_id.to_s } + net.each_terminal { |p| dnames << p.device_class.name + ":" + p.terminal_id.to_s } assert_equal(dnames, [ "DC:1", "DC:0" ]) - net.each_port { |p| assert_equal(p.net.name, "NET") } + net.each_terminal { |p| assert_equal(p.net.name, "NET") } - d1.disconnect_port(1) - assert_equal(d1.net_for_port(1).inspect, "nil") + d1.disconnect_terminal(1) + assert_equal(d1.net_for_terminal(1).inspect, "nil") dnames = [] - net.each_port { |p| dnames << p.device.name + ":" + p.port_def.name } + net.each_terminal { |p| dnames << p.device.name + ":" + p.terminal_def.name } assert_equal(dnames, [ "D2:A" ]) - net.each_port { |p| assert_equal(p.net.name, "NET") } + net.each_terminal { |p| assert_equal(p.net.name, "NET") } net.clear - assert_equal(d1.net_for_port(1).inspect, "nil") - assert_equal(d1.net_for_port(0).inspect, "nil") + assert_equal(d1.net_for_terminal(1).inspect, "nil") + assert_equal(d1.net_for_terminal(0).inspect, "nil") end @@ -320,30 +320,30 @@ class DBNetlist_TestClass < TestBase dc.description = "A device class" assert_equal(dc.description, "A device class") - pd = RBA::DevicePortDefinition::new("A", "Port A") - dc.add_port(pd) + pd = RBA::DeviceTerminalDefinition::new("A", "Terminal A") + dc.add_terminal(pd) assert_equal(pd.id, 0) assert_equal(pd.name, "A") - assert_equal(pd.description, "Port A") + assert_equal(pd.description, "Terminal A") - pd = RBA::DevicePortDefinition::new + pd = RBA::DeviceTerminalDefinition::new pd.name = "B" - pd.description = "Port B" - dc.add_port(pd) + pd.description = "Terminal B" + dc.add_terminal(pd) assert_equal(pd.id, 1) assert_equal(pd.name, "B") - assert_equal(pd.description, "Port B") + assert_equal(pd.description, "Terminal B") names = [] - dc.port_definitions.each { |pd| names << pd.name } + dc.terminal_definitions.each { |pd| names << pd.name } assert_equal(names, [ "A", "B" ]) - dc.clear_ports + dc.clear_terminals names = [] - dc.port_definitions.each { |pd| names << pd.name } + dc.terminal_definitions.each { |pd| names << pd.name } assert_equal(names, []) pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1", 2.0) diff --git a/testdata/ruby/dbNetlistProperty.rb b/testdata/ruby/dbNetlistProperty.rb index 49bee637c..3b1075485 100644 --- a/testdata/ruby/dbNetlistProperty.rb +++ b/testdata/ruby/dbNetlistProperty.rb @@ -46,31 +46,31 @@ class DBNetlistProperty_TestClass < TestBase end - def test_3_DevicePort + def test_3_DeviceTerminal - np = RBA::DevicePortProperty::new - assert_equal(np.is_a?(RBA::DevicePortProperty), true) - assert_equal(np.is_a?(RBA::DevicePortProperty), true) - assert_equal(np.to_s, "port") + np = RBA::DeviceTerminalProperty::new + assert_equal(np.is_a?(RBA::DeviceTerminalProperty), true) + assert_equal(np.is_a?(RBA::DeviceTerminalProperty), true) + assert_equal(np.to_s, "terminal") dc = RBA::GenericDeviceClass::new - dp = RBA::DevicePortDefinition::new + dp = RBA::DeviceTerminalDefinition::new dp.name = "A" - dc.add_port(dp) + dc.add_terminal(dp) dp.name = "B" - dc.add_port(dp) + dc.add_terminal(dp) c = RBA::Circuit::new d = c.create_device(dc, "D") n = c.create_net("NET") - d.connect_port(0, n) + d.connect_terminal(0, n) - # there is no other way to produce a NetPortRef object yet - n.each_port { |p| np.port_ref = p } + # there is no other way to produce a NetTerminalRef object yet + n.each_terminal { |p| np.terminal_ref = p } - assert_equal(np.to_s, "port:D:A") - assert_equal(np.port_ref.device.name, "D") + assert_equal(np.to_s, "terminal:D:A") + assert_equal(np.terminal_ref.device.name, "D") end From 3d9712c53afe93bd56371673ac64af92aa458dc4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Dec 2018 20:46:06 +0100 Subject: [PATCH 111/335] WIP: implementation of device class and GSI bindings. Added some network attributes and predicates (is_floating, is_internal, pin_count, terminal_count) --- src/db/db/dbNetlist.cc | 2 +- src/db/db/dbNetlist.h | 26 +++++++++++- src/db/db/dbNetlistDeviceClasses.cc | 4 +- src/db/db/gsiDeclDbNetlist.cc | 63 ++++++++++++++++++++++++++++- 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 5ee840c20..6c9b1cf2b 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -654,7 +654,7 @@ void Circuit::purge_nets () { std::vector nets_to_be_purged; for (net_iterator n = begin_nets (); n != end_nets (); ++n) { - if (n->floating ()) { + if (n->is_floating ()) { nets_to_be_purged.push_back (n.operator-> ()); } } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index d6dcbad75..1842b9444 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -455,11 +455,35 @@ public: /** * @brief Returns true, if the net is floating (has no or only a single connection) */ - bool floating () const + bool is_floating () const { return (m_pins.size () + m_terminals.size ()) < 2; } + /** + * @brief Returns true, if the net is an internal node (connects two terminals only) + */ + bool is_internal () const + { + return m_pins.size () == 0 && m_terminals.size () == 2; + } + + /** + * @brief Returns the number of pins connected + */ + size_t pin_count () const + { + return m_pins.size (); + } + + /** + * @brief Returns the number of terminals connected + */ + size_t terminal_count () const + { + return m_terminals.size (); + } + private: friend class Circuit; diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index ce09bec1f..f97cca187 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -41,13 +41,13 @@ bool DeviceClassTwoTerminalDevice::combine_devices (Device *a, Device *b) const parallel (a, b); - } else if (na2 == nb1 || na2 == nb2) { + } else if ((na2 == nb1 || na2 == nb2) && na2->is_internal ()) { // serial a(B) to b(A or B) serial (a, b); a->connect_terminal (1, (na2 == nb1 ? nb2 : nb1)); - } else if (na1 == nb1 || na1 == nb2) { + } else if ((na1 == nb1 || na1 == nb2) && na1->is_internal ()) { // serial a(A) to b(A or B) serial (a, b); diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 8a12b5c14..3da7b4207 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -277,6 +277,20 @@ Class decl_dbNet ("db", "Net", "@brief Iterates over all terminals the net connects.\n" "Terminals connect devices. Terminal connections are described by \\NetTerminalRef " "objects." + ) + + gsi::method ("is_floating?", &db::Net::is_floating, + "@brief Returns true, if the net is floating.\n" + "Floating nets are those who don't have any or only a single connection (pin_count + terminal_count < 2)." + ) + + gsi::method ("is_internal?", &db::Net::is_internal, + "@brief Returns true, if the net is an internal net.\n" + "Internal nets are those which connect exactly two terminals and nothing else (pin_count = 0 and terminal_count == 2)." + ) + + gsi::method ("pin_count", &db::Net::pin_count, + "@brief Returns the number of pins connected by this net.\n" + ) + + gsi::method ("terminal_count", &db::Net::terminal_count, + "@brief Returns the number of terminals connected by this net.\n" ), "@brief A single net.\n" "A net connects multiple pins or terminals together. Pins are either " @@ -435,7 +449,7 @@ class GenericDeviceClass { public: GenericDeviceClass () - : db::DeviceClass () + : db::DeviceClass (), m_supports_parallel_combination (true), m_supports_serial_combination (true) { // .. nothing yet .. } @@ -449,8 +463,31 @@ public: } } -private: + virtual bool supports_parallel_combination () const + { + return m_supports_parallel_combination; + } + + virtual bool supports_serial_combination () const + { + return m_supports_serial_combination; + } + + void set_supports_parallel_combination (bool f) + { + m_supports_parallel_combination = f; + } + + void set_supports_serial_combination (bool f) + { + m_supports_serial_combination = f; + } + gsi::Callback cb_combine_devices; + +private: + bool m_supports_parallel_combination; + bool m_supports_serial_combination; }; } @@ -499,6 +536,28 @@ Class decl_GenericDeviceClass (decl_dbDeviceClass, "db", "Ge ) + gsi::method ("description=", &GenericDeviceClass::set_description, gsi::arg ("description"), "@brief Sets the description of the device\n" + ) + + gsi::callback ("combine_devices", &GenericDeviceClass::combine_devices, &GenericDeviceClass::cb_combine_devices, gsi::arg ("a"), gsi::arg ("b"), + "@brief Combines two devices.\n" + "This method shall test, whether the two devices can be combined. Both devices " + "are guaranteed to share the same device class (self). " + "If they cannot be combined, this method shall do nothing and return false. " + "If they can be combined, this method shall reconnect the nets of the first " + "device and entirely disconnect the nets of the second device. " + "It shall combine the parameters of both devices into the first. " + "The second device will be deleted afterwards.\n" + ) + + gsi::method ("supports_parallel_combination=", &GenericDeviceClass::set_supports_parallel_combination, gsi::arg ("f"), + "@brief Specifies whether the device supports parallel device combination.\n" + "Parallel device combination means that all terminals of two combination candidates are connected to the same nets. " + "If the device does not support this combination mode, this predicate can be set to false. This will make the device " + "extractor skip the combination test in parallel mode and improve performance somewhat." + ) + + gsi::method ("supports_serial_combination=", &GenericDeviceClass::set_supports_serial_combination, gsi::arg ("f"), + "@brief Specifies whether the device supports serial device combination.\n" + "Serial device combination means that the devices are connected by internal nodes. " + "If the device does not support this combination mode, this predicate can be set to false. This will make the device " + "extractor skip the combination test in serial mode and improve performance somewhat." ), "@brief A generic device class\n" "This class allows building generic device classes. Specificially, terminals can be defined " From 195324295d79146f4357bec407613dd9b6b6feda Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Dec 2018 20:56:08 +0100 Subject: [PATCH 112/335] WIP: tests for new net predicates. --- src/db/unit_tests/dbNetlistTests.cc | 26 ++++++++++++++++++++++ testdata/ruby/dbNetlist.rb | 34 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 881c5aa61..8fdc67ffb 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -533,6 +533,11 @@ TEST(6_Net) n.clear (); EXPECT_EQ (n.name (), ""); EXPECT_EQ (int (n.cluster_id ()), 0); + + EXPECT_EQ (n.pin_count (), 0); + EXPECT_EQ (n.terminal_count (), 0); + EXPECT_EQ (n.is_floating (), true); + EXPECT_EQ (n.is_internal (), false); } TEST(7_NetTerminalsEditing) @@ -558,9 +563,19 @@ TEST(7_NetTerminalsEditing) d1->connect_terminal (0, n1); d1->connect_terminal (1, n2); + EXPECT_EQ (n1->terminal_count (), 1); + EXPECT_EQ (n1->pin_count (), 0); + EXPECT_EQ (n1->is_floating (), true); + EXPECT_EQ (n1->is_internal (), false); + d2->connect_terminal (1, n1); d2->connect_terminal (0, n2); + EXPECT_EQ (n1->terminal_count (), 2); + EXPECT_EQ (n1->pin_count (), 0); + EXPECT_EQ (n1->is_floating (), false); + EXPECT_EQ (n1->is_internal (), true); + EXPECT_EQ (d1->net_for_terminal (0), n1); EXPECT_EQ (d1->net_for_terminal (1), n2); EXPECT_EQ (d2->net_for_terminal (0), n2); @@ -637,12 +652,23 @@ TEST(8_NetSubCircuitsEditing) c.add_net (n2); c.connect_pin (0, n1); + + EXPECT_EQ (n1->terminal_count (), 0); + EXPECT_EQ (n1->pin_count (), 1); + EXPECT_EQ (n1->is_floating (), true); + EXPECT_EQ (n1->is_internal (), false); + EXPECT_EQ (c.net_for_pin (0), n1); EXPECT_EQ (c.net_for_pin (1), 0); sc1->connect_pin (0, n1); sc1->connect_pin (1, n2); + EXPECT_EQ (n1->terminal_count (), 0); + EXPECT_EQ (n1->pin_count (), 2); + EXPECT_EQ (n1->is_floating (), false); + EXPECT_EQ (n1->is_internal (), false); + sc2->connect_pin (1, n1); sc2->connect_pin (0, n2); diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 08fe2d127..5d9d503a9 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -185,11 +185,33 @@ class DBNetlist_TestClass < TestBase net = c.create_net("NET") + assert_equal(net.is_floating?, true) + assert_equal(net.is_internal?, false) + assert_equal(net.terminal_count, 0) + assert_equal(net.pin_count, 0) + d1.connect_terminal(1, net) + + assert_equal(net.is_floating?, true) + assert_equal(net.is_internal?, false) + assert_equal(net.terminal_count, 1) + assert_equal(net.pin_count, 0) + + d1.connect_terminal(0, net) + + assert_equal(net.is_floating?, false) # not really, but this simple approach tells us so ... + assert_equal(net.is_internal?, true) + assert_equal(net.terminal_count, 2) + assert_equal(net.pin_count, 0) + + d1.disconnect_terminal(0) + assert_equal(net.terminal_count, 1) + assert_equal(d1.net_for_terminal(1).name, "NET") assert_equal(d1.net_for_terminal(0).inspect, "nil") d2.connect_terminal(0, net) + assert_equal(net.terminal_count, 2) dnames = [] net.each_terminal { |p| dnames << p.device.name + ":" + p.terminal_def.name } @@ -260,12 +282,24 @@ class DBNetlist_TestClass < TestBase assert_equal(ccn, [ "CC", "CC" ]) net = c.create_net("NET") + assert_equal(net.pin_count, 0) + assert_equal(net.terminal_count, 0) + assert_equal(net.is_floating?, true) + assert_equal(net.is_internal?, false) sc1.connect_pin(1, net) + assert_equal(net.pin_count, 1) + assert_equal(net.terminal_count, 0) + assert_equal(net.is_floating?, true) + assert_equal(net.is_internal?, false) assert_equal(sc1.net_for_pin(1).name, "NET") assert_equal(sc1.net_for_pin(0).inspect, "nil") sc2.connect_pin(0, net) + assert_equal(net.pin_count, 2) + assert_equal(net.terminal_count, 0) + assert_equal(net.is_floating?, false) + assert_equal(net.is_internal?, false) cnames = [] net.each_pin { |p| cnames << p.subcircuit.name + ":" + p.pin.name } From 88c60420d08b5e6aae840df7741326581603cccc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Dec 2018 00:34:34 +0100 Subject: [PATCH 113/335] WIP: fixed verbosity of some output. --- src/db/db/dbDeepShapeStore.cc | 5 +++-- src/db/unit_tests/dbNetlistDeviceExtractorTests.cc | 0 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 src/db/unit_tests/dbNetlistDeviceExtractorTests.cc diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 76ea5dfcb..358d07c6e 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -291,7 +291,7 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator // Build the working hierarchy from the recursive shape iterator try { - tl::SelfTimer timer (tl::to_string (tr ("Building working hierarchy"))); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Building working hierarchy"))); m_layouts[layout_index]->builder.set_shape_receiver (&clip); db::RecursiveShapeIterator (si).push (& m_layouts[layout_index]->builder); @@ -318,7 +318,8 @@ DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db db::HierarchyBuilder &original_builder = m_layouts [deep_layer.layout_index ()]->builder; - // derive a cell mapping for source to target. We employ a + // Derive a cell mapping for source to target. We reuse any existing mapping for returning the + // shapes into the original layout. DeliveryMappingCacheKey key (deep_layer.layout_index (), tl::id_of (into_layout), into_cell); diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc new file mode 100644 index 000000000..e69de29bb From c75a41a1a413ab672384938f0b0af64856f9e181 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Dec 2018 00:34:57 +0100 Subject: [PATCH 114/335] WIP: fixed verbosity of some output. --- src/db/db/dbHierNetworkProcessor.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 2793eb707..22463d780 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1152,7 +1152,7 @@ template void hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) { - tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Computing shape clusters"))); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing shape clusters"))); std::set called; cell.collect_called_cells (called); @@ -1161,7 +1161,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou // first build all local clusters { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing local shape clusters"))); + tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Computing local shape clusters"))); tl::RelativeProgress progress (tl::to_string (tr ("Computing local clusters")), called.size (), 1); for (std::set::const_iterator c = called.begin (); c != called.end (); ++c) { @@ -1173,7 +1173,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou // build the hierarchical connections bottom-up and for all cells whose children are computed already { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing hierarchical shape clusters"))); + tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Computing hierarchical shape clusters"))); tl::RelativeProgress progress (tl::to_string (tr ("Computing hierarchical clusters")), called.size (), 1); std::set done; @@ -1213,10 +1213,10 @@ void hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) { std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); - if (tl::verbosity () >= 30) { + if (tl::verbosity () >= 40) { tl::log << msg; } - tl::SelfTimer (tl::verbosity () >= 31, msg); + tl::SelfTimer (tl::verbosity () >= 41, msg); connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; local.build_clusters (cell, shape_flags, conn); @@ -1236,10 +1236,10 @@ void hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn) { std::string msg = tl::to_string (tr ("Computing hierarchical clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); - if (tl::verbosity () >= 30) { + if (tl::verbosity () >= 40) { tl::log << msg; } - tl::SelfTimer (tl::verbosity () >= 31, msg); + tl::SelfTimer (tl::verbosity () >= 41, msg); connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; @@ -1267,7 +1267,7 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // handle instance to instance connections { - tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Instance to instance treatment"))); + tl::SelfTimer timer (tl::verbosity () >= 51, tl::to_string (tr ("Instance to instance treatment"))); db::box_scanner bs; @@ -1281,7 +1281,7 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // handle local to instance connections { - tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Local to instance treatment"))); + tl::SelfTimer timer (tl::verbosity () >= 51, tl::to_string (tr ("Local to instance treatment"))); db::box_scanner2, unsigned int, db::Instance, unsigned int> bs2; From 5c18424c108d027522203718e340047c9534cf15 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Dec 2018 00:36:19 +0100 Subject: [PATCH 115/335] WIP: fixed a comment typo. --- src/db/db/dbRegion.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index c2969f45e..8b3673feb 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1686,7 +1686,9 @@ public: } /** - * @brief Less operator + * @brief Inserts the region into the given layout, cell and layer + * If the region is a hierarchical region, the hierarchy is copied into the + * layout's hierarchy. */ void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const { From bfc0e24d50d9d20c3c2766ee16348855916b86ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Dec 2018 00:36:34 +0100 Subject: [PATCH 116/335] WIP: fixed some compiler warnings. --- src/db/unit_tests/dbNetlistTests.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 8fdc67ffb..d8cd7907c 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -534,8 +534,8 @@ TEST(6_Net) EXPECT_EQ (n.name (), ""); EXPECT_EQ (int (n.cluster_id ()), 0); - EXPECT_EQ (n.pin_count (), 0); - EXPECT_EQ (n.terminal_count (), 0); + EXPECT_EQ (n.pin_count (), size_t (0)); + EXPECT_EQ (n.terminal_count (), size_t (0)); EXPECT_EQ (n.is_floating (), true); EXPECT_EQ (n.is_internal (), false); } @@ -563,16 +563,16 @@ TEST(7_NetTerminalsEditing) d1->connect_terminal (0, n1); d1->connect_terminal (1, n2); - EXPECT_EQ (n1->terminal_count (), 1); - EXPECT_EQ (n1->pin_count (), 0); + EXPECT_EQ (n1->terminal_count (), size_t (1)); + EXPECT_EQ (n1->pin_count (), size_t (0)); EXPECT_EQ (n1->is_floating (), true); EXPECT_EQ (n1->is_internal (), false); d2->connect_terminal (1, n1); d2->connect_terminal (0, n2); - EXPECT_EQ (n1->terminal_count (), 2); - EXPECT_EQ (n1->pin_count (), 0); + EXPECT_EQ (n1->terminal_count (), size_t (2)); + EXPECT_EQ (n1->pin_count (), size_t (0)); EXPECT_EQ (n1->is_floating (), false); EXPECT_EQ (n1->is_internal (), true); @@ -653,8 +653,8 @@ TEST(8_NetSubCircuitsEditing) c.connect_pin (0, n1); - EXPECT_EQ (n1->terminal_count (), 0); - EXPECT_EQ (n1->pin_count (), 1); + EXPECT_EQ (n1->terminal_count (), size_t (0)); + EXPECT_EQ (n1->pin_count (), size_t (1)); EXPECT_EQ (n1->is_floating (), true); EXPECT_EQ (n1->is_internal (), false); @@ -664,8 +664,8 @@ TEST(8_NetSubCircuitsEditing) sc1->connect_pin (0, n1); sc1->connect_pin (1, n2); - EXPECT_EQ (n1->terminal_count (), 0); - EXPECT_EQ (n1->pin_count (), 2); + EXPECT_EQ (n1->terminal_count (), size_t (0)); + EXPECT_EQ (n1->pin_count (), size_t (2)); EXPECT_EQ (n1->is_floating (), false); EXPECT_EQ (n1->is_internal (), false); From 4e899d7d6ca7b5d3262764d79ee8636d2d47dcc5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Dec 2018 00:36:54 +0100 Subject: [PATCH 117/335] WIP: Added a test layout --- testdata/algo/device_extract_l1.gds | Bin 0 -> 2762 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testdata/algo/device_extract_l1.gds diff --git a/testdata/algo/device_extract_l1.gds b/testdata/algo/device_extract_l1.gds new file mode 100644 index 0000000000000000000000000000000000000000..fa837f4e2a317771de909276f480b397e7cf8199 GIT binary patch literal 2762 zcma);(MuFj6vofa&fb|_S2L5skSHmrP>VDpBvIQ+F*MvwY7z9Ymmo+YqCzOak|3xj z>p_C-!M7j~^eH03ryheM_8$lqd~(U1zB6}@Gb7HJpugpu^WAgrJ?Ea46dA@1YEBqd zi89no2k0cV1wWH*sH=nhM2*Q*Pv5h1Ltj6X&*zp;Exaifs5up_%P3VC?HkS$nKqHC zHOL@UXAn_qebR9@?>SEO7?UXRlgM~36g!j5y}sTI>Z=C)Yxo77Mew&zu~*mP^w>#} z^~l@BdY@vaVw4`SEzqoKI*VYH^J|JdIzQ_;zhzDNMt0P=RdruO7E7VxeB**z%ViF|}>#P>V z_J^(1;PBWPGBZS(2I-Y7_eS-OyO3M^oxPc|&JpsegNbP(tbmiej96h?jI86GP;Mx2 z!o)4^SNBvz-_VScSlOe6#>Qz$n4XHdZ`@8R&Na>?N>8C~uZ3b~#^_7P)v0gUe5>mv z6nnBWq?dZ>_qL86QOLWF(($|bBNRI`N{<`e=lVg`A%Cq~Z_w z7)KKp9EWLf@1sYAVpoOG{_Wsb_9y%s4&j)voo~k6JGjd%8WO zPlWSxpJ;vE53Lu99eos>cPU?wy|&=~9%!0lAm)Wo>|lf*_x29=H?L`7_EsKaKOV)d z=0keR&yU(Z!TqdgI-gL-NBHbf?A4VxJv2kC$KEfo-ly1UC|;jl()!GMtrvDKz}S*;g}y{231vs?3Szd;F}DEi%@KB@^*D-?y9AyHe?z;03_*9#~Re|zmR2k|{Q zIyih~1d*6#c7vXZ?i9l=`A6np$d|bm?Q2|!_8lrm`%d&m`#QoCHMExtrD)y2>1f~R zbhK~$yYIu8<^LaKI=l01`#h+8(er@$Kl5N?0CBx|51@ryMa%=C*j2B-doO=ro)AqO zXT2utCYQCIDfVsp(0kf=g}6E72dx>r#}_`uuI57d1A3yH9jw>1X0ZP@)@zDAPCs#1 z>zS;ZyzP_qp5N^~?>*rBey2~A5pNTBdj6uI} literal 0 HcmV?d00001 From ec65d293e38893eaedd84d49490e322fd34b39a6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Dec 2018 00:38:35 +0100 Subject: [PATCH 118/335] WIP: moved some GSI specific methods into the C++ classes because they are of general use. --- src/db/db/dbNetlist.cc | 57 +++++++++++++++++++++++++++++++++++ src/db/db/dbNetlist.h | 34 +++++++++++++++++++++ src/db/db/gsiDeclDbNetlist.cc | 54 +++++++++------------------------ 3 files changed, 105 insertions(+), 40 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 6c9b1cf2b..53e08b6d4 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -157,6 +157,19 @@ void Device::set_parameter_value (size_t param_id, double v) m_parameters [param_id] = v; } +double Device::parameter_value (const std::string &name) const +{ + return device_class () ? parameter_value (device_class ()->parameter_id_for_name (name)) : 0.0; +} + +void Device::set_parameter_value (const std::string &name, double v) +{ + if (device_class ()) { + set_parameter_value (device_class ()->parameter_id_for_name (name), v); + } +} + + // -------------------------------------------------------------------------------- // SubCircuit class implementation @@ -888,6 +901,50 @@ const DeviceParameterDefinition *DeviceClass::parameter_definition (size_t id) c } } +bool DeviceClass::has_parameter_with_name (const std::string &name) const +{ + const std::vector &pd = parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->name () == name) { + return true; + } + } + return false; +} + +size_t DeviceClass::parameter_id_for_name (const std::string &name) const +{ + const std::vector &pd = parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->name () == name) { + return i->id (); + } + } + throw tl::Exception (tl::to_string (tr ("Invalid parameter name")) + ": '" + name + "'"); +} + +bool DeviceClass::has_terminal_with_name (const std::string &name) const +{ + const std::vector &td = terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + if (i->name () == name) { + return true; + } + } + return false; +} + +size_t DeviceClass::terminal_id_for_name (const std::string &name) const +{ + const std::vector &td = terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + if (i->name () == name) { + return i->id (); + } + } + throw tl::Exception (tl::to_string (tr ("Invalid terminal name")) + ": '" + name + "'"); +} + // -------------------------------------------------------------------------------- // Netlist class implementation diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 1842b9444..e674a46d7 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -635,6 +635,18 @@ public: */ void set_parameter_value (size_t param_id, double v); + /** + * @brief Gets the value for the parameter with the given name + * If the name is not valid, an exception is thrown. + */ + double parameter_value (const std::string &name) const; + + /** + * @brief Sets the value for the parameter with the given name + * If the name is not valid, an exception is thrown. + */ + void set_parameter_value (const std::string &name, double v); + private: friend class Circuit; friend class Net; @@ -1432,6 +1444,28 @@ public: */ const DeviceParameterDefinition *parameter_definition (size_t id) const; + /** + * @brief Returns true, if the device has a parameter with the given name + */ + bool has_parameter_with_name (const std::string &name) const; + + /** + * @brief Returns the parameter ID for the parameter with the given name + * If the name is invalid, an exception is thrown. + */ + size_t parameter_id_for_name (const std::string &name) const; + + /** + * @brief Returns true, if the device has a terminal with the given name + */ + bool has_terminal_with_name (const std::string &name) const; + + /** + * @brief Returns the parameter ID for the terminal with the given name + * If the name is invalid, an exception is thrown. + */ + size_t terminal_id_for_name (const std::string &name) const; + /** * @brief Clears the circuit */ diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 3da7b4207..9cee3ee5e 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -47,40 +47,6 @@ static void device_disconnect_terminal (db::Device *device, size_t terminal_id) device->connect_terminal (terminal_id, 0); } -static bool device_has_param_with_name (const db::DeviceClass *device_class, const std::string &name) -{ - const std::vector &pd = device_class->parameter_definitions (); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (i->name () == name) { - return true; - } - } - return false; -} - -static size_t device_param_id_for_name (const db::DeviceClass *device_class, const std::string &name) -{ - if (device_class) { - const std::vector &pd = device_class->parameter_definitions (); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (i->name () == name) { - return i->id (); - } - } - } - throw tl::Exception (tl::to_string (tr ("Invalid parameter name")) + ": '" + name + "'"); -} - -static double device_parameter_value (const db::Device *device, const std::string &name) -{ - return device->parameter_value (device_param_id_for_name (device->device_class (), name)); -} - -static void device_set_parameter_value (db::Device *device, const std::string &name, double value) -{ - return device->set_parameter_value (device_param_id_for_name (device->device_class (), name), value); -} - Class decl_dbDevice ("db", "Device", gsi::method ("device_class", &db::Device::device_class, "@brief Gets the device class the device belongs to.\n" @@ -103,17 +69,17 @@ Class decl_dbDevice ("db", "Device", gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal, gsi::arg ("terminal_id"), "@brief Disconnects the given terminal from any net.\n" ) + - gsi::method ("parameter", &db::Device::parameter_value, gsi::arg ("param_id"), + gsi::method ("parameter", (double (db::Device::*) (size_t) const) &db::Device::parameter_value, gsi::arg ("param_id"), "@brief Gets the parameter value for the given parameter ID." ) + - gsi::method ("set_parameter", &db::Device::set_parameter_value, gsi::arg ("param_id"), gsi::arg ("value"), + gsi::method ("set_parameter", (void (db::Device::*) (size_t, double)) &db::Device::set_parameter_value, gsi::arg ("param_id"), gsi::arg ("value"), "@brief Sets the parameter value for the given parameter ID." ) + - gsi::method_ext ("parameter", &gsi::device_parameter_value, gsi::arg ("param_name"), + gsi::method ("parameter", (double (db::Device::*) (const std::string &) const) &db::Device::parameter_value, gsi::arg ("param_name"), "@brief Gets the parameter value for the given parameter name.\n" "If the parameter name is not valid, an exception is thrown." ) + - gsi::method_ext ("set_parameter", &gsi::device_set_parameter_value, gsi::arg ("param_name"), gsi::arg ("value"), + gsi::method ("set_parameter", (void (db::Device::*) (const std::string &, double)) &db::Device::set_parameter_value, gsi::arg ("param_name"), gsi::arg ("value"), "@brief Sets the parameter value for the given parameter name.\n" "If the parameter name is not valid, an exception is thrown." ), @@ -417,13 +383,21 @@ Class decl_dbDeviceClass ("db", "DeviceClass", "Parameter definition IDs are used in some places to reference a specific parameter of a device. " "This method obtains the corresponding definition object." ) + - gsi::method_ext ("has_parameter", &gsi::device_has_param_with_name, gsi::arg ("name"), + gsi::method ("has_parameter", &db::DeviceClass::has_parameter_with_name, gsi::arg ("name"), "@brief Returns true, if the device class has a parameter with the given name.\n" ) + - gsi::method_ext ("parameter_id", &gsi::device_param_id_for_name, gsi::arg ("name"), + gsi::method ("parameter_id", &db::DeviceClass::parameter_id_for_name, gsi::arg ("name"), "@brief Returns the parameter ID of the parameter with the given name.\n" "An exception is thrown if there is no parameter with the given name. Use \\has_parameter to check " "whether the name is a valid parameter name." + ) + + gsi::method ("has_terminal", &db::DeviceClass::has_terminal_with_name, gsi::arg ("name"), + "@brief Returns true, if the device class has a terminal with the given name.\n" + ) + + gsi::method ("terminal_id", &db::DeviceClass::terminal_id_for_name, gsi::arg ("name"), + "@brief Returns the terminal ID of the terminal with the given name.\n" + "An exception is thrown if there is no terminal with the given name. Use \\has_terminal to check " + "whether the name is a valid terminal name." ), "@brief A class describing a specific type of device.\n" "Device class objects live in the context of a \\Netlist object. After a " From f342bdd83a10a9b511bad7c18d0c5e914df83506 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Dec 2018 00:39:34 +0100 Subject: [PATCH 119/335] WIP: added some convenience methods. --- src/db/db/dbNetlistDeviceExtractor.cc | 44 ++++++++++++++++++++++++++- src/db/db/dbNetlistDeviceExtractor.h | 42 ++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 153bcd0b4..ca5f738ab 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -21,9 +21,10 @@ */ #include "dbNetlistDeviceExtractor.h" -#include "dbHierNetworkProcessor.h" #include "dbNetlistProperty.h" #include "dbRegion.h" +#include "dbHierNetworkProcessor.h" +#include "dbDeepRegion.h" namespace db { @@ -55,6 +56,47 @@ static void insert_into_region (const db::PolygonRef &s, const db::ICplxTrans &t region.insert (s.obj ().transformed (tr * db::ICplxTrans (s.trans ()))); } +void NetlistDeviceExtractor::extract (const std::vector regions) +{ + tl_assert (! regions.empty ()); + + const db::Layout *layout = 0; + const db::Cell *cell = 0; + std::vector layers; + layers.reserve (regions.size ()); + + for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { + + // TODO: this is clumsy ... + db::DeepRegion *dr = dynamic_cast ((*r)->delegate ()); + tl_assert (dr != 0); + + db::DeepLayer dl = dr->deep_layer (); + tl_assert (dl.layout () != 0); + tl_assert (dl.initial_cell () != 0); + + if (! layout) { + layout = dl.layout (); + } else { + tl_assert (layout == dl.layout ()); + } + + if (! cell) { + cell = dl.initial_cell (); + } else { + tl_assert (cell == dl.initial_cell ()); + } + + layers.push_back (dl.layer ()); + + } + + // NOTE: the const_cast's are there because the extraction will annotate the layout with port + // shapes. That's not part of the initial design, where the underlying deep shape store is + // immutable from the outside. But we know what we're doing. + extract (const_cast (*layout), const_cast (*cell), layers); +} + void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers) { typedef db::PolygonRef shape_type; diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index fd929ce00..a80e788a2 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -25,8 +25,8 @@ #include "dbCommon.h" #include "dbNetlist.h" -#include "dbHierNetworkProcessor.h" #include "dbLayout.h" +#include "dbHierNetworkProcessor.h" #include "gsiObject.h" @@ -81,6 +81,19 @@ public: */ void extract (Layout &layout, Cell &cell, const std::vector &layers); + /** + * @brief Extracts the devices from a list of regions + * + * This method behaves identical to the other "extract" method, but accepts + * regions for input. + * + * As a requirement, the layout and initial cell of all of the regions + * has to be identical. + * + * Currently, the regions have to be deep regions. + */ + void extract (const std::vector regions); + /** * @brief Creates the device classes * At least one device class needs to be defined. Use "register_device_class" to register @@ -148,6 +161,33 @@ protected: return mp_layout->dbu (); } + /** + * @brief Gets the layout the shapes are taken from + * NOTE: this method is provided for testing purposes mainly. + */ + db::Layout *layout () + { + return mp_layout; + } + + /** + * @brief Gets the layout the shapes are taken from (const version) + * NOTE: this method is provided for testing purposes mainly. + */ + const db::Layout *layout () const + { + return mp_layout; + } + + /** + * @brief Gets the cell index of the current cell + * NOTE: this method is provided for testing purposes mainly. + */ + db::cell_index_type cell_index () const + { + return m_cell_index; + } + private: tl::weak_ptr m_netlist; db::Layout *mp_layout; From 1db5ad016c4e720793f19186c581b0c0713bc263 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Dec 2018 00:39:46 +0100 Subject: [PATCH 120/335] WIP: added first tests for device extraction. --- .../dbNetlistDeviceExtractorTests.cc | 311 ++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 3 +- 2 files changed, 313 insertions(+), 1 deletion(-) diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index e69de29bb..cfd4b7a38 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -0,0 +1,311 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlistDeviceExtractor.h" +#include "dbNetlistDeviceClasses.h" +#include "dbHierNetworkProcessor.h" +#include "dbLayout.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" +#include "dbStream.h" +#include "dbDeepRegion.h" +#include "dbDeepShapeStore.h" +#include "dbReader.h" +#include "dbWriter.h" +#include "dbCommonReader.h" + +#include "tlUnitTest.h" +#include "tlString.h" +#include "tlFileUtils.h" + +#include + +class MOSFETExtractor + : public db::NetlistDeviceExtractor +{ +public: + MOSFETExtractor (db::Layout *debug_out) + : db::NetlistDeviceExtractor (), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) + { + if (mp_debug_out) { + m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); + m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); + } + } + + virtual void create_device_classes () + { + db::DeviceClassMOS3Transistor *pmos_class = new db::DeviceClassMOS3Transistor (); + pmos_class->set_name ("PMOS"); + register_device_class (pmos_class); + + db::DeviceClassMOS3Transistor *nmos_class = new db::DeviceClassMOS3Transistor (); + nmos_class->set_name ("NMOS"); + register_device_class (nmos_class); + } + + virtual db::Connectivity get_connectivity (const db::Layout & /*layout*/, const std::vector &layers) const + { + tl_assert (layers.size () == 3); + + unsigned int lpdiff = layers [0]; + unsigned int lndiff = layers [1]; + unsigned int gate = layers [2]; + + // The layer definition is pdiff, ndiff, gate + db::Connectivity conn; + // collect all connected pdiff + conn.connect (lpdiff, lpdiff); + // collect all connected ndiff + conn.connect (lndiff, lndiff); + // collect all connected gate shapes + conn.connect (gate, gate); + // connect gate with pdiff + conn.connect (lpdiff, gate); + // connect gate with ndiff + conn.connect (lndiff, gate); + return conn; + } + + virtual void extract_devices (const std::vector &layer_geometry) + { + const db::Region &rpdiff = layer_geometry [0]; + const db::Region &rndiff = layer_geometry [1]; + const db::Region &rgates = layer_geometry [2]; + + for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) { + + db::Region rgate (*p); + db::Region rpdiff_on_gate = rpdiff.selected_interacting (rgate); + db::Region rndiff_on_gate = rndiff.selected_interacting (rgate); + + if (! rpdiff_on_gate.empty () && ! rndiff_on_gate.empty ()) { + error (tl::to_string (tr ("Gate shape touches both ndiff and pdiff - ignored")), *p); + } else if (rpdiff_on_gate.empty () && rndiff_on_gate.empty ()) { + error (tl::to_string (tr ("Gate shape touches neither ndiff and pdiff - ignored")), *p); + } else { + + db::Region &diff = (rpdiff_on_gate.empty () ? rndiff_on_gate : rpdiff_on_gate); + + if (diff.size () != 2) { + error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (diff.size ())), *p); + continue; + } + + db::Edges edges (rgate.edges () & diff.edges ()); + if (edges.size () != 2) { + error (tl::sprintf (tl::to_string (tr ("Expected two edges interacting gate/diff (found %d) - width and length may be incorrect")), int (edges.size ())), *p); + continue; + } + + if (! p->is_box ()) { + error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p); + } + + db::Device *device = create_device (! rpdiff_on_gate.empty () ? 0 /*pdiff*/ : 1 /*ndiff*/); + + device->set_parameter_value ("W", dbu () * edges.length () * 0.5); + device->set_parameter_value ("L", dbu () * (p->perimeter () - edges.length ()) * 0.5); + + int index = 0; + for (db::Region::const_iterator d = diff.begin (); !d.at_end () && index < 2; ++d, ++index) { + + // count the number of gate shapes attached to this shape and distribute the area of the + // diffusion area to the number of gates + int n = rgates.selected_interacting (db::Region (*d)).size (); + tl_assert (n > 0); + + device->set_parameter_value (index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n)); + + } + + // @@@ create terminals + + device_out (device, diff, rgate); + + } + + } + } + + void error (const std::string &msg) + { + // @@@ TODO: move this to device extractor + tl::error << tr ("Error in cell '") << cell_name () << "': " << msg; + } + + void error (const std::string &msg, const db::Polygon &poly) + { + // @@@ TODO: move this to device extractor + tl::error << tr ("Error in cell '") << cell_name () << "': " << msg << " (" << poly.to_string () << ")"; + } + + void error (const std::string &msg, const db::Region ®ion) + { + // @@@ TODO: move this to device extractor + tl::error << tr ("Error in cell '") << cell_name () << "': " << msg << " (" << region.to_string () << ")"; + } + + std::string cell_name () const + { + // @@@ TODO: move this to device extractor + return layout ()->cell_name (cell_index ()); + } + +private: + db::Layout *mp_debug_out; + unsigned int m_ldiff, m_lgate; + + void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) + { + if (! mp_debug_out) { + return; + } + + std::string cn = layout ()->cell_name (cell_index ()); + std::pair target_cp = mp_debug_out->cell_by_name (cn.c_str ()); + tl_assert (target_cp.first); + + db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->name ()).c_str ()); + mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ())); + + db::Cell &device_cell = mp_debug_out->cell (dci); + for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) { + device_cell.shapes (m_ldiff).insert (*p); + } + for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) { + device_cell.shapes (m_lgate).insert (*p); + } + + std::string ps; + const std::vector &pd = device->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (! ps.empty ()) { + ps += ","; + } + ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ())); + } + device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ()))); + } +}; + +TEST(1_DeviceNetExtraction) +{ + bool write_debug = true; + + db::Layout ly; + unsigned int nwell, active, poly; + + db::LayerProperties p; + db::LayerMap lmap; + + p.layer = 1; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), nwell = ly.insert_layer ()); + ly.set_properties (nwell, p); + + p.layer = 2; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), active = ly.insert_layer ()); + ly.set_properties (active, p); + + p.layer = 3; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), poly = ly.insert_layer ()); + ly.set_properties (poly, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + + // original layers + db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); + db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss); + db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + + // derived regions + db::Region rgate = ractive & rpoly; + db::Region rsd = ractive - rgate; + db::Region rpdiff = rsd & rnwell; + db::Region rndiff = rsd - rnwell; + + if (write_debug) { + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rgate.insert_into (&ly, tc.cell_index (), lgate); + rsd.insert_into (&ly, tc.cell_index (), lsd); + rpdiff.insert_into (&ly, tc.cell_index (), lpdiff); + rndiff.insert_into (&ly, tc.cell_index (), lndiff); + + } + + db::DeepRegion *dr = dynamic_cast (rnwell.delegate ()); + db::DeepLayer dl = dr->deep_layer (); + dl.layout (); + dl.initial_cell (); + dl.layer (); + + db::Netlist nl; + + MOSFETExtractor ex (write_debug ? &ly : 0); + ex.initialize (&nl); + + std::vector region_ptrs; + region_ptrs.push_back (&rpdiff); + region_ptrs.push_back (&rndiff); + region_ptrs.push_back (&rgate); + ex.extract (region_ptrs); + + if (write_debug) { + + std::string fn (tl::testtmp ()); + fn = tl::combine_path (fn, "debug-1_DeviceNetExtraction.gds"); + + tl::OutputStream stream (fn); + db::SaveLayoutOptions options; + db::Writer writer (options); + writer.write (ly, stream); + + tl::log << "Device layer debug file written to: " << tl::absolute_file_path (fn); + + } +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 63a29945c..ac3cd3382 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -60,7 +60,8 @@ SOURCES = \ dbDeepShapeStoreTests.cc \ dbHierNetworkProcessorTests.cc \ dbNetlistPropertyTests.cc \ - dbNetlistTests.cc + dbNetlistTests.cc \ + dbNetlistDeviceExtractorTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC From 024907e7ef36303fa0290f68747a99b345f71c36 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Dec 2018 10:02:34 +0100 Subject: [PATCH 121/335] WIP: first test for device extraction. --- .../dbNetlistDeviceExtractorTests.cc | 71 +++++++++--------- testdata/algo/device_extract_au1.gds | Bin 0 -> 3088 bytes 2 files changed, 34 insertions(+), 37 deletions(-) create mode 100644 testdata/algo/device_extract_au1.gds diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index cfd4b7a38..de6fae79f 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -33,6 +33,7 @@ #include "dbReader.h" #include "dbWriter.h" #include "dbCommonReader.h" +#include "dbTestSupport.h" #include "tlUnitTest.h" #include "tlString.h" @@ -105,7 +106,11 @@ public: error (tl::to_string (tr ("Gate shape touches neither ndiff and pdiff - ignored")), *p); } else { - db::Region &diff = (rpdiff_on_gate.empty () ? rndiff_on_gate : rpdiff_on_gate); + bool is_pmos = ! rpdiff_on_gate.empty (); + + db::Region &diff = (is_pmos ? rpdiff_on_gate : rndiff_on_gate); + unsigned int terminal_layer_index = (is_pmos ? 0 : 1); + unsigned int device_class_index = (is_pmos ? 0 /*PMOS*/ : 1 /*NMOS*/); if (diff.size () != 2) { error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (diff.size ())), *p); @@ -122,25 +127,26 @@ public: error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p); } - db::Device *device = create_device (! rpdiff_on_gate.empty () ? 0 /*pdiff*/ : 1 /*ndiff*/); + db::Device *device = create_device (device_class_index); device->set_parameter_value ("W", dbu () * edges.length () * 0.5); device->set_parameter_value ("L", dbu () * (p->perimeter () - edges.length ()) * 0.5); - int index = 0; - for (db::Region::const_iterator d = diff.begin (); !d.at_end () && index < 2; ++d, ++index) { + int diff_index = 0; + for (db::Region::const_iterator d = diff.begin (); !d.at_end () && diff_index < 2; ++d, ++diff_index) { // count the number of gate shapes attached to this shape and distribute the area of the - // diffusion area to the number of gates + // diffusion region to the number of gates int n = rgates.selected_interacting (db::Region (*d)).size (); tl_assert (n > 0); - device->set_parameter_value (index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n)); + device->set_parameter_value (diff_index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n)); + + define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_layer_index, *d); } - // @@@ create terminals - + // output the device for debugging device_out (device, diff, rgate); } @@ -262,30 +268,25 @@ TEST(1_DeviceNetExtraction) db::Region rpdiff = rsd & rnwell; db::Region rndiff = rsd - rnwell; - if (write_debug) { + // return the computed layers into the original layout and write it for debugging purposes - // return the computed layers into the original layout and write it for debugging purposes + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion - unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate - unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain - unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion - unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + rgate.insert_into (&ly, tc.cell_index (), lgate); + rsd.insert_into (&ly, tc.cell_index (), lsd); + rpdiff.insert_into (&ly, tc.cell_index (), lpdiff); + rndiff.insert_into (&ly, tc.cell_index (), lndiff); - rgate.insert_into (&ly, tc.cell_index (), lgate); - rsd.insert_into (&ly, tc.cell_index (), lsd); - rpdiff.insert_into (&ly, tc.cell_index (), lpdiff); - rndiff.insert_into (&ly, tc.cell_index (), lndiff); - - } - - db::DeepRegion *dr = dynamic_cast (rnwell.delegate ()); - db::DeepLayer dl = dr->deep_layer (); - dl.layout (); - dl.initial_cell (); - dl.layer (); + // perform the extraction db::Netlist nl; + // NOTE: the device extractor will add more debug layers for the transistors: + // 20/0 -> Diffusion + // 21/0 -> Gate MOSFETExtractor ex (write_debug ? &ly : 0); ex.initialize (&nl); @@ -293,19 +294,15 @@ TEST(1_DeviceNetExtraction) region_ptrs.push_back (&rpdiff); region_ptrs.push_back (&rndiff); region_ptrs.push_back (&rgate); + ex.extract (region_ptrs); - if (write_debug) { + // compare the collected test data - std::string fn (tl::testtmp ()); - fn = tl::combine_path (fn, "debug-1_DeviceNetExtraction.gds"); + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1.gds"); - tl::OutputStream stream (fn); - db::SaveLayoutOptions options; - db::Writer writer (options); - writer.write (ly, stream); - - tl::log << "Device layer debug file written to: " << tl::absolute_file_path (fn); - - } + db::compare_layouts (_this, ly, au); } diff --git a/testdata/algo/device_extract_au1.gds b/testdata/algo/device_extract_au1.gds new file mode 100644 index 0000000000000000000000000000000000000000..612c2c6629ed7b708e5c6c8b8c4407170aae7f02 GIT binary patch literal 3088 zcmb_eJ#Q015FMW{>$9;#$OjTpgrGnI$s!-N2@wjyC=x^jCvq%6C=w0yR1pP8kJ3>iW!+$F(aN=G3uN9mHv4G zFcwb)=UMT?6H zm3IEejBy{*KlMd@NPiTUK6+GaGFvg4EmDTCyu~vnC~>KjJ!8!8Me(#^JY!zP{%FRy z51KLRpZj5?eNbHXakyfgcC)pN#~<%Fu=gKVTE$iIR_?9RZNxKqE+}zAp0OhPP(J3X z)8t0cQFJ5Ng<*0f0Ih#+69VKDb{1#F^yp;OXl@OU z-MIV<&k9}7xa$~CndB7=-{~QC9J78pYuFX~IH8Y^^Mr3U%p2E?HrZ_$C4Mj;KH&`4Q6a?e z74qjSN*pmcV^06ySMa)Yv?eUPS%*;KUG*z=|9|zp;PB;rO!bY&U|mM+$$YsHI{hd3 zq`%4c^o17dm*A6bT*u5HdYpV=@fVfkx&GlE-kxt=Tf_lGr-%mGaZsC&^*=;}X`h-8%Ui0Tx-rF217>4j0$x@{; literal 0 HcmV?d00001 From 8f568641e027dd58f1333321766f9d02aa5fd440 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 00:20:21 +0100 Subject: [PATCH 122/335] WIP: some refactoring, added label recognition to net extraction, test enhancements. --- src/db/db/dbDeepShapeStore.cc | 66 +- src/db/db/dbDeepShapeStore.h | 47 ++ src/db/db/dbHierNetworkProcessor.cc | 30 +- src/db/db/dbHierNetworkProcessor.h | 18 +- src/db/db/dbHierarchyBuilder.cc | 28 +- src/db/db/dbHierarchyBuilder.h | 5 +- src/db/db/dbNetlistDeviceExtractor.cc | 5 +- src/db/db/dbNetlistDeviceExtractor.h | 1 + src/db/unit_tests/dbDeepShapeStoreTests.cc | 35 ++ .../dbNetlistDeviceExtractorTests.cc | 564 ++++++++++++++++-- testdata/algo/device_extract_au1.gds | Bin 3088 -> 8774 bytes 11 files changed, 738 insertions(+), 61 deletions(-) diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 358d07c6e..eea29866c 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -177,7 +177,7 @@ struct DeepShapeStore::LayoutHolder static size_t s_instance_count = 0; DeepShapeStore::DeepShapeStore () - : m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16) + : m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16), m_text_property_name (), m_text_enlargement (-1) { ++s_instance_count; } @@ -192,6 +192,16 @@ DeepShapeStore::~DeepShapeStore () m_layouts.clear (); } +void DeepShapeStore::set_text_enlargement (int enl) +{ + m_text_enlargement = enl; +} + +void DeepShapeStore::set_text_property_name (const tl::Variant &pn) +{ + m_text_property_name = pn; +} + bool DeepShapeStore::is_valid_layout_index (unsigned int n) const { return (n < (unsigned int) m_layouts.size () && m_layouts[n] != 0); @@ -280,11 +290,14 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator } - unsigned int layer_index = m_layouts[layout_index]->layout.insert_layer (); - m_layouts[layout_index]->builder.set_target_layer (layer_index); + db::Layout &layout = m_layouts[layout_index]->layout; + db::HierarchyBuilder &builder = m_layouts[layout_index]->builder; + + unsigned int layer_index = layout.insert_layer (); + builder.set_target_layer (layer_index); // The chain of operators for producing clipped and reduced polygon references - db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& m_layouts[layout_index]->layout); + db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& layout, m_text_enlargement, m_text_property_name); db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count); db::ClippingHierarchyBuilderShapeReceiver clip (&red); @@ -293,35 +306,36 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Building working hierarchy"))); - m_layouts[layout_index]->builder.set_shape_receiver (&clip); - db::RecursiveShapeIterator (si).push (& m_layouts[layout_index]->builder); - m_layouts[layout_index]->builder.set_shape_receiver (0); + builder.set_shape_receiver (&clip); + db::RecursiveShapeIterator (si).push (& builder); + builder.set_shape_receiver (0); } catch (...) { - m_layouts[layout_index]->builder.set_shape_receiver (0); + builder.set_shape_receiver (0); throw; } return DeepLayer (this, layout_index, layer_index); } -void -DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) +const db::CellMapping & +DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell) { - const db::Layout *source_layout = deep_layer.layout (); + const db::Layout *source_layout = &m_layouts [layout_index]->layout; if (source_layout->begin_top_down () == source_layout->end_top_cells ()) { // empty source - nothing to do. - return; + static db::CellMapping cm; + return cm; } db::cell_index_type source_top = *source_layout->begin_top_down(); - db::HierarchyBuilder &original_builder = m_layouts [deep_layer.layout_index ()]->builder; + db::HierarchyBuilder &original_builder = m_layouts [layout_index]->builder; // Derive a cell mapping for source to target. We reuse any existing mapping for returning the // shapes into the original layout. - DeliveryMappingCacheKey key (deep_layer.layout_index (), tl::id_of (into_layout), into_cell); + DeliveryMappingCacheKey key (layout_index, tl::id_of (into_layout), into_cell); std::map::iterator cm = m_delivery_mapping_cache.find (key); if (cm == m_delivery_mapping_cache.end ()) { @@ -375,16 +389,34 @@ DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db } - // Actually copy the shapes + return cm->second; +} +void +DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) +{ + const db::Layout *source_layout = deep_layer.layout (); + if (source_layout->begin_top_down () == source_layout->end_top_cells ()) { + // empty source - nothing to do. + return; + } + + // prepare the transformation db::ICplxTrans trans (source_layout->dbu () / into_layout->dbu ()); + // prepare a layer map std::map lm; lm.insert (std::make_pair (deep_layer.layer (), into_layer)); + // prepare a cell mapping + const db::CellMapping &cm = cell_mapping_to_original (deep_layer.layout_index (), into_layout, into_cell); + + // prepare a vector with the source cells std::vector source_cells; - source_cells.push_back (source_top); - db::copy_shapes (*into_layout, *source_layout, trans, source_cells, cm->second.table (), lm); + source_cells.push_back (*source_layout->begin_top_down()); + + // actually copy the shapes + db::copy_shapes (*into_layout, *source_layout, trans, source_cells, cm.table (), lm); } } diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 10553c268..d944260a9 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -202,6 +202,15 @@ public: */ void insert (const DeepLayer &layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer); + /** + * @brief Gets the cell mapping suitable to returning a layout from the deep shape store into the original layout hierarchy + * + * If necessary, this method will modify the original layout and add new cells. + * "layout_index" is the layout to return to it's original. "into_layout" is the original layout, "into_cell" + * the original cell. + */ + const db::CellMapping &cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell); + /** * @brief For testing */ @@ -274,6 +283,42 @@ public: return m_max_area_ratio; } + /** + * @brief Sets the text property name + * + * If set to a non-null variant, text strings are attached to the generated boxes + * as properties with this particular name. This option has an effect only if the + * text_enlargement property is not negative. + * By default, the name is empty. + */ + void set_text_property_name (const tl::Variant &pn); + + /** + * @brief Gets the text property name + */ + const tl::Variant &text_property_name () const + { + return m_text_property_name; + } + + /** + * @brief Sets the text enlargement value + * + * If set to a non-negative value, text objects are converted to boxes with the + * given enlargement (width = 2 * enlargement). The box centers are identical + * to the original location of the text. + * If this value is negative (the default), texts are ignored. + */ + void set_text_enlargement (int enl); + + /** + * @brief Gets the text enlargement value + */ + int text_enlargement () const + { + return m_text_enlargement; + } + private: friend class DeepLayer; @@ -295,6 +340,8 @@ private: int m_threads; double m_max_area_ratio; size_t m_max_vertex_count; + tl::Variant m_text_property_name; + int m_text_enlargement; tl::Mutex m_lock; struct DeliveryMappingCacheKey diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 22463d780..8096572e0 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1016,13 +1016,37 @@ private: } else if (x->second != y->second) { // join two superclusters - x->second->insert (y->second->begin (), y->second->end ()); - for (typename std::set::const_iterator i = y->second->begin (); i != y->second->end (); ++i) { + std::set &yset = *y->second; + x->second->insert (yset.begin (), yset.end ()); + for (typename std::set::const_iterator i = yset.begin (); i != yset.end (); ++i) { m_cm2join_map [*i] = x->second; } - y->second->clear (); + yset.clear (); // TODO: no longer required, but we can't delete it, as we just have a pointer .. replace pointer by iterator! } + +#if defined(DEBUG_HIER_NETWORK_PROCESSOR) + // concistency check for debugging + for (typename std::map *>::const_iterator j = m_cm2join_map.begin (); j != m_cm2join_map.end (); ++j) { + tl_assert (j->second->find (j->first) != j->second->end ()); + } + + for (typename std::list >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) { + for (typename std::set::const_iterator j = i->begin(); j != i->end(); ++j) { + tl_assert(m_cm2join_map.find (*j) != m_cm2join_map.end ()); + tl_assert(m_cm2join_map[*j] == i.operator->()); + } + } + + // the sets must be disjunct + std::set all; + for (typename std::list >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) { + for (typename std::set::const_iterator j = i->begin(); j != i->end(); ++j) { + tl_assert(all.find (*j) == all.end()); + all.insert(*j); + } + } +#endif } /** diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index d3afe5412..d27a39174 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -123,7 +123,7 @@ public: typedef typename T::box_type box_type; typedef db::unstable_box_tree > tree_type; typedef typename tree_type::flat_iterator shape_iterator; - typedef unsigned int attr_id; + typedef size_t attr_id; typedef std::set attr_set; typedef attr_set::const_iterator attr_iterator; @@ -317,6 +317,14 @@ public: return m_clusters.end (); } + /** + * @brief Gets a value indicating whether the cluster set is empty + */ + bool empty () const + { + return m_clusters.empty (); + } + /** * @brief Gets the clusters touching a given region */ @@ -554,6 +562,14 @@ public: return m_connections.end (); } + /** + * @brief Gets a value indicating whether the cluster set is empty + */ + bool empty () const + { + return local_clusters::empty () && m_connections.empty (); + } + /** * @brief Returns true, if the given cluster ID is a root cluster */ diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 2f8b999e0..4a64de4d8 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -492,18 +492,40 @@ ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db // --------------------------------------------------------------------------------------------- -PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout) - : mp_layout (layout) +PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement, const tl::Variant &text_prop_name) + : mp_layout (layout), m_text_enlargement (text_enlargement), m_make_text_prop (false), m_text_prop_id (0) { - // nothing yet .. + if (! text_prop_name.is_nil ()) { + m_text_prop_id = layout->properties_repository ().prop_name_id (text_prop_name); + m_make_text_prop = true; + } } void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) { if (shape.is_box () || shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { + db::Polygon poly; shape.polygon (poly); target->insert (db::PolygonRef (poly, mp_layout->shape_repository ())); + + } else if (shape.is_text () && m_text_enlargement >= 0) { + + db::Polygon poly (shape.text_trans () * db::Box (-m_text_enlargement, -m_text_enlargement, m_text_enlargement, m_text_enlargement)); + db::PolygonRef pref (poly, mp_layout->shape_repository ()); + + if (m_make_text_prop) { + + db::PropertiesRepository::properties_set ps; + ps.insert (std::make_pair (m_text_prop_id, tl::Variant (shape.text_string ()))); + db::properties_id_type pid = mp_layout->properties_repository ().properties_id (ps); + + target->insert (db::PolygonRefWithProperties (pref, pid)); + + } else { + target->insert (pref); + } + } } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 37176b340..6b7cd6448 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -135,7 +135,7 @@ class DB_PUBLIC PolygonReferenceHierarchyBuilderShapeReceiver : public HierarchyBuilderShapeReceiver { public: - PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout); + PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement = -1, const tl::Variant &text_prop_name = tl::Variant ()); virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); @@ -143,6 +143,9 @@ public: private: db::Layout *mp_layout; + int m_text_enlargement; + bool m_make_text_prop; + db::property_names_id_type m_text_prop_id; }; /** diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index ca5f738ab..e0134c619 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -103,6 +103,7 @@ void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const db::ShapeIterator::flags_type shape_iter_flags = db::ShapeIterator::Polygons; mp_layout = &layout; + m_layers = layers; // terminal properties are kept in property index 0 m_propname_id = mp_layout->properties_repository ().prop_name_id (tl::Variant (int (0))); @@ -207,9 +208,11 @@ Device *NetlistDeviceExtractor::create_device (unsigned int device_class_index) return device; } -void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Polygon &polygon) +void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t geometry_index, const db::Polygon &polygon) { tl_assert (mp_layout != 0); + tl_assert (geometry_index < m_layers.size ()); + unsigned int layer_index = m_layers [geometry_index]; // Build a property set for the DeviceTerminalProperty db::PropertiesRepository::properties_set ps; diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index a80e788a2..e98ab6def 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -195,6 +195,7 @@ private: db::cell_index_type m_cell_index; db::Circuit *mp_circuit; std::vector m_device_classes; + std::vector m_layers; unsigned int m_device_name_index; }; diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc index eb21e8020..cc3ee008d 100644 --- a/src/db/unit_tests/dbDeepShapeStoreTests.cc +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -149,3 +149,38 @@ TEST(2_RefCounting) dl2 = db::DeepLayer (); EXPECT_EQ (store.is_valid_layout_index (lyi1), false); } + +TEST(3_TextTreatment) +{ + db::DeepShapeStore store; + db::Layout layout; + + unsigned int l1 = layout.insert_layer (); + db::cell_index_type c1 = layout.add_cell ("C1"); + layout.cell (c1).shapes (l1).insert (db::Text ("TEXT", db::Trans (db::Vector (1000, 2000)))); + + db::DeepLayer dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).empty (), true); + + store.set_text_enlargement (1); + dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).size (), size_t (1)); + EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999)"); + + store.set_text_property_name (tl::Variant ("text")); + dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).size (), size_t (1)); + EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999) prop_id=1"); + + const db::Layout *dss_layout = store.const_layout (0); + db::PropertiesRepository::properties_set ps = dss_layout->properties_repository ().properties (1); + EXPECT_EQ (ps.size (), size_t (1)); + EXPECT_EQ (dss_layout->properties_repository ().prop_name (ps.begin ()->first).to_string (), "text"); + EXPECT_EQ (ps.begin ()->second.to_string (), "TEXT"); +} diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index de6fae79f..273f5d218 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -34,20 +34,24 @@ #include "dbWriter.h" #include "dbCommonReader.h" #include "dbTestSupport.h" +#include "dbNetlistProperty.h" +#include "dbCellMapping.h" #include "tlUnitTest.h" #include "tlString.h" #include "tlFileUtils.h" #include +#include class MOSFETExtractor : public db::NetlistDeviceExtractor { public: - MOSFETExtractor (db::Layout *debug_out) + MOSFETExtractor (db::Netlist &nl, db::Layout *debug_out) : db::NetlistDeviceExtractor (), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) { + initialize (&nl); if (mp_debug_out) { m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); @@ -67,11 +71,12 @@ public: virtual db::Connectivity get_connectivity (const db::Layout & /*layout*/, const std::vector &layers) const { - tl_assert (layers.size () == 3); + tl_assert (layers.size () == 4); unsigned int lpdiff = layers [0]; unsigned int lndiff = layers [1]; unsigned int gate = layers [2]; + // not used for device recognition: poly (3), but used for producing the gate terminals // The layer definition is pdiff, ndiff, gate db::Connectivity conn; @@ -109,7 +114,8 @@ public: bool is_pmos = ! rpdiff_on_gate.empty (); db::Region &diff = (is_pmos ? rpdiff_on_gate : rndiff_on_gate); - unsigned int terminal_layer_index = (is_pmos ? 0 : 1); + unsigned int terminal_geometry_index = (is_pmos ? 0 : 1); + unsigned int gate_geometry_index = 3; unsigned int device_class_index = (is_pmos ? 0 /*PMOS*/ : 1 /*NMOS*/); if (diff.size () != 2) { @@ -142,10 +148,12 @@ public: device->set_parameter_value (diff_index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n)); - define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_layer_index, *d); + define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_geometry_index, *d); } + define_terminal (device, device->device_class ()->terminal_id_for_name ("G"), gate_geometry_index, *p); + // output the device for debugging device_out (device, diff, rgate); @@ -215,52 +223,456 @@ private: } }; +static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) +{ + unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +// @@@ TODO: move somewhere else +static unsigned int layer_of (const db::Region ®ion) +{ + // TODO: this is clumsy ... + db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + + return dr->deep_layer ().layer (); +} + +// @@@ TODO: move somewhere else +static db::Layout &layout_of (const db::Region ®ion) +{ + // TODO: this is clumsy ... + db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + + db::DeepLayer dl = dr->deep_layer (); + tl_assert (dl.layout () != 0); + return *dl.layout (); +} + +// @@@ TODO: move somewhere else +static db::Layout &layout_of (const std::vector ®ions) +{ + db::Layout *layout = 0; + for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { + db::Layout &l = layout_of (**r); + if (! layout) { + layout = &l; + } else { + tl_assert (layout == &l); + } + } + + tl_assert (layout != 0); + return *layout; +} + +// @@@ TODO: move somewhere else +static db::Cell &cell_of (const db::Region ®ion) +{ + // TODO: this is clumsy ... + db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + + db::DeepLayer dl = dr->deep_layer (); + tl_assert (dl.initial_cell () != 0); + return *dl.initial_cell (); +} + +// @@@ TODO: move somewhere else +static db::Cell &cell_of (const std::vector ®ions) +{ + db::Cell *cell = 0; + + for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { + db::Cell &c = cell_of (**r); + if (! cell) { + cell = &c; + } else { + tl_assert (cell == &c); + } + } + + tl_assert (cell != 0); + return *cell; +} + +// @@@ TODO: move somewhere else +class NetExtractor +{ +public: + typedef db::hier_clusters hier_clusters_type; + typedef db::connected_clusters connected_clusters_type; + typedef db::local_cluster local_cluster_type; + + NetExtractor () + { + // .. nothing yet .. + } + + void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl) + { + tl::Variant terminal_property_name (0); // @@@ take somewhere else + + // only works for singular-layout stores currently. This rules out layers from different sources + // and clipping. + tl_assert (dss.layouts () == 1); + const db::Layout *layout = dss.const_layout (0); + + tl_assert (layout->cells () != 0); + const db::Cell &cell = layout->cell (*layout->begin_top_down ()); + + // gets the text annotation property ID + std::pair text_annot_name_id (false, 0); + if (! dss.text_property_name ().is_nil ()) { + text_annot_name_id = layout->properties_repository ().get_id_of_name (dss.text_property_name ()); + } + + // gets the device terminal annotation property ID + std::pair terminal_annot_name_id (false, 0); + if (! terminal_property_name.is_nil ()) { + terminal_annot_name_id = layout->properties_repository ().get_id_of_name (terminal_property_name); + } + + m_net_clusters.build (*layout, cell, db::ShapeIterator::Polygons, conn); + + std::map circuits; + // some circuits may be there because of device extraction + for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { + // @@@ TODO: what if the circuits don't have a cell index? + circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); + } + + std::map > pins_per_cluster; + + for (db::Layout::bottom_up_const_iterator cid = layout->begin_bottom_up (); cid != layout->end_bottom_up (); ++cid) { + + const connected_clusters_type &clusters = m_net_clusters.clusters_per_cell (*cid); + if (clusters.empty ()) { + continue; + } + + // a cell makes a new circuit (or uses an existing one) + + db::Circuit *circuit = 0; + + std::map::const_iterator k = circuits.find (*cid); + if (k == circuits.end ()) { + circuit = new db::Circuit (); + nl->add_circuit (circuit); + circuit->set_name (layout->cell_name (*cid)); + circuit->set_cell_index (*cid); + circuits.insert (std::make_pair (*cid, circuit)); + } else { + circuit = k->second; + } + + std::map &c2p = pins_per_cluster [*cid]; + + std::map subcircuits; + + for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { + + db::Net *net = new db::Net (); + net->set_cluster_id (*c); + // @@@ TODO: set name + circuit->add_net (net); + + if (! clusters.is_root (*c)) { + + // a non-root cluster makes a pin + db::Pin pin (net->name ()); + size_t pin_id = circuit->add_pin (pin).id (); + net->add_pin (db::NetPinRef (pin_id)); + c2p.insert (std::make_pair (*c, pin_id)); + circuit->connect_pin (pin_id, net); + + } + + const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (*c); + for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + + db::SubCircuit *subcircuit = 0; + db::cell_index_type ccid = i->inst ().inst_ptr.cell_index (); + + std::map::const_iterator j = subcircuits.find (i->inst ()); + if (j == subcircuits.end ()) { + + // make subcircuit if required + + std::map::const_iterator k = circuits.find (ccid); + tl_assert (k != circuits.end ()); // because we walk bottom-up + + // @@@ name? + subcircuit = new db::SubCircuit (k->second); + db::CplxTrans dbu_trans (layout->dbu ()); + subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); + circuit->add_sub_circuit (subcircuit); + subcircuits.insert (std::make_pair (i->inst (), subcircuit)); + + } else { + subcircuit = j->second; + } + + // create the pin connection to the subcircuit + std::map >::const_iterator icc2p = pins_per_cluster.find (ccid); + tl_assert (icc2p != pins_per_cluster.end ()); + std::map::const_iterator ip = icc2p->second.find (i->id ()); + tl_assert (ip != icc2p->second.end ()); + subcircuit->connect_pin (ip->second, net); + + } + + // collect the properties - we know that the cluster attributes are property ID's because the + // cluster processor converts shape property IDs to attributes + const local_cluster_type &lc = clusters.cluster_by_id (*c); + for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { + + // @@@ TODO: needs refactoring!!! + // -> use two distinct and reserved property name ID's for names (=string) and device terminal refs (=single number) instead + // of the scary DeviceTerminalProperty (pointer!!!) + const db::PropertiesRepository::properties_set &ps = layout->properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + + if (terminal_annot_name_id.first && j->first == terminal_annot_name_id.second) { + + if (j->second.is_user ()) { + const db::NetlistProperty *np = &j->second.to_user (); + const db::DeviceTerminalProperty *tp = dynamic_cast (np); + const db::NetNameProperty *nnp = dynamic_cast (np); + if (tp) { + const_cast (tp->terminal_ref ().device ())->connect_terminal (tp->terminal_ref ().terminal_id (), net); + } else if (nnp) { + net->set_name (nnp->name ()); + } + } + + } else if (text_annot_name_id.first && j->first == text_annot_name_id.second) { + + std::string n = j->second.to_string (); + if (! n.empty ()) { + if (! net->name ().empty ()) { + n = net->name () + "," + n; + } + net->set_name (n); + } + + } + + } + + } + + } + + } + } + + const hier_clusters_type clusters () const + { + return m_net_clusters; + } + +private: + hier_clusters_type m_net_clusters; +}; + +// @@@ TODO: move this somewhere else +static std::string net_name (const db::Net *net) +{ + if (! net) { + return "(null)"; + } else if (net->name ().empty ()) { + if (net->cluster_id () > std::numeric_limits::max () / 2) { + return "$I" + tl::to_string ((std::numeric_limits::max () - net->cluster_id ()) + 1); + } else { + return "$" + tl::to_string (net->cluster_id ()); + } + } else { + return net->name (); + } +} + +// @@@ TODO: refactor. This is inefficient. Give an ID automatically. +static std::string device_name (const db::Device &device, const db::Circuit &circuit) +{ + if (device.name ().empty ()) { + int id = 1; + for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices () && d.operator-> () != &device; ++d, ++id) + ; + return "$" + tl::to_string (id); + } else { + return device.name (); + } +} + +// @@@ TODO: refactor. This is inefficient. Give an ID automatically. +static std::string subcircuit_name (const db::SubCircuit &subcircuit, const db::Circuit &circuit) +{ + if (subcircuit.name ().empty ()) { + int id = 1; + for (db::Circuit::const_sub_circuit_iterator d = circuit.begin_sub_circuits (); d != circuit.end_sub_circuits () && d.operator-> () != &subcircuit; ++d, ++id) + ; + return "$" + tl::to_string (id); + } else { + return subcircuit.name (); + } +} + +// @@@ TODO: refactor. This is inefficient. Give an ID automatically. +static std::string pin_name (const db::Pin &pin, const db::Circuit &circuit) +{ + if (pin.name ().empty ()) { + int id = 1; + for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins () && p.operator-> () != &pin; ++p, ++id) + ; + return "$" + tl::to_string (id); + } else { + return pin.name (); + } +} + +// @@@ TODO: move this somewhere else + +static void dump_nets (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +{ + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + + db::Cell &cell = ly.cell (cmap.cell_mapping (c->cell_index ())); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + const db::local_cluster &lc = clusters.clusters_per_cell (c->cell_index ()).cluster_by_id (n->cluster_id ()); + + bool any_shapes = false; + for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any_shapes; ++m) { + any_shapes = ! lc.begin (m->first).at_end (); + } + + if (any_shapes) { + + std::string nn = "NET_" + c->name () + "_" + net_name (n.operator-> ()); + db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ())); + cell.insert (db::CellInstArray (db::CellInst (net_cell.cell_index ()), db::Trans ())); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + db::Shapes &target = net_cell.shapes (m->second); + for (db::local_cluster::shape_iterator s = lc.begin (m->first); !s.at_end (); ++s) { + target.insert (*s); + } + } + + } + + } + + } +} + +static std::string netlist2string (const db::Netlist &nl) +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + + std::string ps; + for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) { + if (! ps.empty ()) { + ps += ","; + } + ps += pin_name (*p, *c) + "=" + net_name (c->net_for_pin (p->id ())); + } + + res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n"; + +// @@@ good for debugging +#if 0 + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n"; + } +#endif + + for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + std::string ts; + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + if (t != td.begin ()) { + ts += ","; + } + ts += t->name () + "=" + net_name (d->net_for_terminal (t->id ())); + } + res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d, *c) + " (" + ts + ")\n"; + } + + for (db::Circuit::const_sub_circuit_iterator sc = c->begin_sub_circuits (); sc != c->end_sub_circuits (); ++sc) { + std::string ps; + const db::SubCircuit &subcircuit = *sc; + for (db::Circuit::const_pin_iterator p = sc->circuit ()->begin_pins (); p != sc->circuit ()->end_pins (); ++p) { + if (p != sc->circuit ()->begin_pins ()) { + ps += ","; + } + const db::Pin &pin = *p; + ps += pin_name (pin, *subcircuit.circuit ()) + "=" + net_name (subcircuit.net_for_pin (pin.id ())); + } + res += std::string (" X") + sc->circuit ()->name () + " " + subcircuit_name (*sc, *c) + " (" + ps + ")\n"; + } + + } + + return res; +} + TEST(1_DeviceNetExtraction) { - bool write_debug = true; - db::Layout ly; - unsigned int nwell, active, poly; - - db::LayerProperties p; db::LayerMap lmap; - p.layer = 1; - p.datatype = 0; - lmap.map (db::LDPair (p.layer, p.datatype), nwell = ly.insert_layer ()); - ly.set_properties (nwell, p); + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); - p.layer = 2; - p.datatype = 0; - lmap.map (db::LDPair (p.layer, p.datatype), active = ly.insert_layer ()); - ly.set_properties (active, p); + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; - p.layer = 3; - p.datatype = 0; - lmap.map (db::LDPair (p.layer, p.datatype), poly = ly.insert_layer ()); - ly.set_properties (poly, p); + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); - db::LoadLayoutOptions options; - options.get_options ().layer_map = lmap; - options.get_options ().create_other_layers = false; - - std::string fn (tl::testsrc ()); - fn = tl::combine_path (fn, "testdata"); - fn = tl::combine_path (fn, "algo"); - fn = tl::combine_path (fn, "device_extract_l1.gds"); - - tl::InputStream stream (fn); - db::Reader reader (stream); - reader.read (ly, options); + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } db::Cell &tc = ly.cell (*ly.begin_top_down ()); db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("LABEL")); // original layers db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss); db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss); + db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss); + db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss); + db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss); + db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss); + db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss); + db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss); + db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss); // derived regions db::Region rgate = ractive & rpoly; @@ -287,16 +699,98 @@ TEST(1_DeviceNetExtraction) // NOTE: the device extractor will add more debug layers for the transistors: // 20/0 -> Diffusion // 21/0 -> Gate - MOSFETExtractor ex (write_debug ? &ly : 0); - ex.initialize (&nl); + MOSFETExtractor ex (nl, &ly); std::vector region_ptrs; region_ptrs.push_back (&rpdiff); region_ptrs.push_back (&rndiff); region_ptrs.push_back (&rgate); + region_ptrs.push_back (&rpoly); ex.extract (region_ptrs); + // perform the net extraction + + NetExtractor net_ex; + + db::Connectivity conn; + // Intra-layer + conn.connect (layer_of (rpdiff)); + conn.connect (layer_of (rndiff)); + conn.connect (layer_of (rpoly)); + conn.connect (layer_of (rdiff_cont)); + conn.connect (layer_of (rpoly_cont)); + conn.connect (layer_of (rmetal1)); + conn.connect (layer_of (rvia1)); + conn.connect (layer_of (rmetal2)); + // Inter-layer + conn.connect (layer_of (rpdiff), layer_of (rdiff_cont)); + conn.connect (layer_of (rndiff), layer_of (rdiff_cont)); + conn.connect (layer_of (rpoly), layer_of (rpoly_cont)); + conn.connect (layer_of (rpoly_cont), layer_of (rmetal1)); + conn.connect (layer_of (rdiff_cont), layer_of (rmetal1)); + conn.connect (layer_of (rmetal1), layer_of (rvia1)); + conn.connect (layer_of (rvia1), layer_of (rmetal2)); + conn.connect (layer_of (rpoly), layer_of (rpoly_lbl)); // attaches labels + conn.connect (layer_of (rmetal1), layer_of (rmetal1_lbl)); // attaches labels + conn.connect (layer_of (rmetal2), layer_of (rmetal2_lbl)); // attaches labels + + // extract the nets + + net_ex.extract_nets (dss, conn, &nl); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + std::map dump_map; + dump_map [layer_of (rpdiff) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [layer_of (rndiff) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // @@@ we should not have to do this -> can be taken from dss? + db::CellMapping cm; + const db::Layout &ex_layout = layout_of (rpoly); + const db::Cell &ex_cell = cell_of (rpoly); + cm.create_from_names_full (ly, tc.cell_index (), ex_layout, ex_cell.cell_index ()); + + // write nets to layout + dump_nets (nl, net_ex.clusters (), ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (netlist2string (nl), + "Circuit RINGO ():\n" + " XINV2 $1 ($1=$I8,$2=FB,$3=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 ($1=FB,$2=$I38,$3=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 ($1=$I19,$2=$I39,$3=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 ($1=$I1,$2=$I40,$3=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 ($1=$I2,$2=$I41,$3=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 ($1=$I3,$2=$I42,$3=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 ($1=$I4,$2=$I43,$3=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 ($1=$I5,$2=$I44,$3=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 ($1=$I6,$2=$I45,$3=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 ($1=$I7,$2=$I46,$3=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 ($1=IN,$2=$2,$3=OUT,$4=$4,$5=$5):\n" + " DPMOS 1 (S=$2,G=IN,D=$5)\n" + " DPMOS 2 (S=$5,G=$2,D=OUT)\n" + " DNMOS 3 (S=$2,G=IN,D=$4)\n" + " DNMOS 4 (S=$4,G=$2,D=OUT)\n" + " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" + " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" + " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" + " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + // compare the collected test data std::string au = tl::testsrc (); diff --git a/testdata/algo/device_extract_au1.gds b/testdata/algo/device_extract_au1.gds index 612c2c6629ed7b708e5c6c8b8c4407170aae7f02..05c3b013fd10004443e7780adcea823a917aa686 100644 GIT binary patch literal 8774 zcmcIqU5Hgx6yEn|&b>3v*ffrTCSfsx!elz*Pa=wAV=3BdFg_IYP!JRnVIibo zMG(}J^&mp_U{66H*i%GA_9N9ppv2HaZ-UTMlg;V-_S);7z0bYp-g9mx4Bxc&{?6Ka z{q42)WFk{8KV{aH%C+B2zq!{Kv&wApe>e4VbmY)}V}>g|dnex9dHAc3Z||DC^3a73 z=4Q>ho^0M=6ipqNU6?&KF*UO=GH$AMW1@kwxn`o3W#}0lti*BrMjYQ+BpOp$59w=) zDkqxi-NsaFrhvY&hQ4nTea4g?h~wpN1mjD;?HAuF6J|9{~B5bPg$)O#8G2ljo0^&_7YRo;6!Sbr0L#ctvE z!^q?-s(i)nfPdZBzjO}zH^(ut|5IanUP(~py_ zzXVlI4AvX)<4EQ)Zpfp*1XbRipVpt|%Xqf_F8C>bVSQKWFZ_H)e};GSV)6b*j2XYO!e|BQ?!`54sCzka75;+WWf4R!yI1XZ5xAK~e|oP5u|vyQ~=zK1F_u;t6| zOmZx5jCZ!!QN~zuJ523ZvO%&yVvN;KsvcvIHL3*8U0Q5TV@PaB&yzclb563)ag2iqF})t#xv{%y$5M8LE32!uRMw>&uOoE z6!C?}*2=!2$W=~sT!w>j6%fR8&yiT0J# z750hNASVJV^(0otE^#jDU0UzfiYiatu{}Okc@f#0^UmhF0zIF+uAuZB|AJ#X0b{;K z5}ZIbIK&>4*qzh-@Yr@)Qf#azf=gsz+~2F~W-tIZqhtgnffF2Oo$iPhcd zRqUlT#fN>0y|gy*^#xyF^c7W}df9K{>kGcV=qsu`^|FshO#V{0bPg3c=S#2iQ;Lrc zJLg{p9KS!&kKT1a?lgJsR?YmbZYrLB_#dx&ypa&~hN7tQ)ZH-Pr*Aqr{<1WX?7yqq ze85lN=BeLyg-gY)CjYc62*&!3+QF7%!xys0?z^2mwrOAX*m&4|1Z&x|n9V!5C421H>Flxj>%o}o zC>ot;EBg$|?bPe9n`Sv*1~oT*&gJM|gId%@|LXA@{i*F!bM~(Rzl-}qP|J$+zs`Oh zzu0}lomt=~XVwBD5@##VEPE&7{~gh49^*m!3Eo87&B8={?fA@$I{~x4hEAFDCMVrI JWNNi){sFFC1yBG0 delta 259 zcmX@+GC@L#fsKKQDS|2yi9SmG-Y#~98e!-KE3pPwXE_?*5eGOu4jGL__92hrGli_B Date: Thu, 27 Dec 2018 00:28:44 +0100 Subject: [PATCH 123/335] WIP: some small refactoring. --- src/db/db/dbHierNetworkProcessor.cc | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 8096572e0..6020ba4be 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -763,8 +763,9 @@ private: hier_clusters *mp_tree; const cell_clusters_box_converter *mp_cbc; const db::Connectivity *mp_conn; - std::map *> m_cm2join_map; - std::list > m_cm2join_sets; + typedef std::list > join_set_list; + std::map m_cm2join_map; + join_set_list m_cm2join_sets; /** * @brief Handles the cluster interactions between two instances or instance arrays @@ -987,8 +988,8 @@ private: */ void mark_to_join (id_type a, id_type b) { - typename std::map *>::const_iterator x = m_cm2join_map.find (a); - typename std::map *>::const_iterator y = m_cm2join_map.find (b); + typename std::map::const_iterator x = m_cm2join_map.find (a); + typename std::map::const_iterator y = m_cm2join_map.find (b); if (x == m_cm2join_map.end ()) { @@ -998,8 +999,8 @@ private: m_cm2join_sets.back ().insert (a); m_cm2join_sets.back ().insert (b); - m_cm2join_map [a] = &m_cm2join_sets.back (); - m_cm2join_map [b] = &m_cm2join_sets.back (); + m_cm2join_map [a] = --m_cm2join_sets.end (); + m_cm2join_map [b] = --m_cm2join_sets.end (); } else { @@ -1016,25 +1017,25 @@ private: } else if (x->second != y->second) { // join two superclusters - std::set &yset = *y->second; - x->second->insert (yset.begin (), yset.end ()); - for (typename std::set::const_iterator i = yset.begin (); i != yset.end (); ++i) { + typename join_set_list::iterator yset = y->second; + x->second->insert (yset->begin (), yset->end ()); + for (typename std::set::const_iterator i = yset->begin (); i != yset->end (); ++i) { m_cm2join_map [*i] = x->second; } - yset.clear (); // TODO: no longer required, but we can't delete it, as we just have a pointer .. replace pointer by iterator! + m_cm2join_sets.erase (yset); } #if defined(DEBUG_HIER_NETWORK_PROCESSOR) // concistency check for debugging - for (typename std::map *>::const_iterator j = m_cm2join_map.begin (); j != m_cm2join_map.end (); ++j) { + for (typename std::map::const_iterator j = m_cm2join_map.begin (); j != m_cm2join_map.end (); ++j) { tl_assert (j->second->find (j->first) != j->second->end ()); } for (typename std::list >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) { for (typename std::set::const_iterator j = i->begin(); j != i->end(); ++j) { tl_assert(m_cm2join_map.find (*j) != m_cm2join_map.end ()); - tl_assert(m_cm2join_map[*j] == i.operator->()); + tl_assert(m_cm2join_map[*j] == i); } } @@ -1047,6 +1048,7 @@ private: } } #endif + } /** From 724d1bc2552cd06757834301c5eca7bbc0f25f94 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 00:32:40 +0100 Subject: [PATCH 124/335] WIP: some small refactoring. --- .../dbNetlistDeviceExtractorTests.cc | 67 +------------------ 1 file changed, 1 insertion(+), 66 deletions(-) diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index 273f5d218..c1a5c14f4 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -240,65 +240,6 @@ static unsigned int layer_of (const db::Region ®ion) return dr->deep_layer ().layer (); } -// @@@ TODO: move somewhere else -static db::Layout &layout_of (const db::Region ®ion) -{ - // TODO: this is clumsy ... - db::DeepRegion *dr = dynamic_cast (region.delegate ()); - tl_assert (dr != 0); - - db::DeepLayer dl = dr->deep_layer (); - tl_assert (dl.layout () != 0); - return *dl.layout (); -} - -// @@@ TODO: move somewhere else -static db::Layout &layout_of (const std::vector ®ions) -{ - db::Layout *layout = 0; - for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { - db::Layout &l = layout_of (**r); - if (! layout) { - layout = &l; - } else { - tl_assert (layout == &l); - } - } - - tl_assert (layout != 0); - return *layout; -} - -// @@@ TODO: move somewhere else -static db::Cell &cell_of (const db::Region ®ion) -{ - // TODO: this is clumsy ... - db::DeepRegion *dr = dynamic_cast (region.delegate ()); - tl_assert (dr != 0); - - db::DeepLayer dl = dr->deep_layer (); - tl_assert (dl.initial_cell () != 0); - return *dl.initial_cell (); -} - -// @@@ TODO: move somewhere else -static db::Cell &cell_of (const std::vector ®ions) -{ - db::Cell *cell = 0; - - for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { - db::Cell &c = cell_of (**r); - if (! cell) { - cell = &c; - } else { - tl_assert (cell == &c); - } - } - - tl_assert (cell != 0); - return *cell; -} - // @@@ TODO: move somewhere else class NetExtractor { @@ -377,7 +318,6 @@ public: db::Net *net = new db::Net (); net->set_cluster_id (*c); - // @@@ TODO: set name circuit->add_net (net); if (! clusters.is_root (*c)) { @@ -757,13 +697,8 @@ TEST(1_DeviceNetExtraction) dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); - // @@@ we should not have to do this -> can be taken from dss? - db::CellMapping cm; - const db::Layout &ex_layout = layout_of (rpoly); - const db::Cell &ex_cell = cell_of (rpoly); - cm.create_from_names_full (ly, tc.cell_index (), ex_layout, ex_cell.cell_index ()); - // write nets to layout + db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); dump_nets (nl, net_ex.clusters (), ly, dump_map, cm); // compare netlist as string From 2171b98bd8b771c8df2e1abfde4f1d89efb13e62 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 00:59:44 +0100 Subject: [PATCH 125/335] WIP: introduced device IDs --- src/db/db/dbNetlist.cc | 44 +++++++++++++++++-- src/db/db/dbNetlist.h | 42 ++++++++++++++++++ src/db/db/gsiDeclDbNetlist.cc | 10 +++++ .../dbNetlistDeviceExtractorTests.cc | 3 +- src/db/unit_tests/dbNetlistTests.cc | 14 ++++++ testdata/ruby/dbNetlist.rb | 3 ++ 6 files changed, 111 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 53e08b6d4..f396b50d7 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -46,6 +46,7 @@ Pin::Pin (const std::string &name) // Device class implementation Device::Device () + : mp_device_class (0), m_id (0) { // .. nothing yet .. } @@ -60,12 +61,13 @@ Device::~Device () } Device::Device (DeviceClass *device_class, const std::string &name) - : mp_device_class (device_class), m_name (name) + : mp_device_class (device_class), m_name (name), m_id (0) { // .. nothing yet .. } Device::Device (const Device &other) + : mp_device_class (0), m_id (0) { operator= (other); } @@ -470,13 +472,13 @@ void Net::set_circuit (Circuit *circuit) // Circuit class implementation Circuit::Circuit () - : mp_netlist (0) + : mp_netlist (0), m_valid_device_id_table (false) { // .. nothing yet .. } Circuit::Circuit (const Circuit &other) - : mp_netlist (0) + : mp_netlist (0), m_valid_device_id_table (false) { operator= (other); } @@ -486,6 +488,7 @@ Circuit &Circuit::operator= (const Circuit &other) if (this != &other) { m_name = other.m_name; + invalidate_device_id_table (); for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { add_pin (*i); @@ -589,12 +592,47 @@ void Circuit::remove_net (Net *net) void Circuit::add_device (Device *device) { + size_t id = 0; + if (! m_devices.empty ()) { + tl_assert (m_devices.back () != 0); + id = m_devices.back ()->id (); + } + device->set_id (id + 1); + m_devices.push_back (device); + invalidate_device_id_table (); } void Circuit::remove_device (Device *device) { m_devices.erase (device); + invalidate_device_id_table (); +} + +void Circuit::validate_device_id_table () +{ + m_device_id_table.clear (); + for (device_iterator d = begin_devices (); d != end_devices (); ++d) { + m_device_id_table.insert (std::make_pair (d->id (), d.operator-> ())); + } + + m_valid_device_id_table = true; +} + +void Circuit::invalidate_device_id_table () +{ + m_valid_device_id_table = false; + m_device_id_table.clear (); +} + +Device *Circuit::device_by_id (size_t id) +{ + if (! m_valid_device_id_table) { + validate_device_id_table (); + } + + std::map::const_iterator d = m_device_id_table.find (id); + return d != m_device_id_table.end () ? d->second : 0; } void Circuit::add_sub_circuit (SubCircuit *sub_circuit) diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index e674a46d7..e9ceec255 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -589,6 +589,17 @@ public: return mp_device_class; } + /** + * @brief Gets the device ID + * The ID is a unique integer which identifies the device. + * It can be used to retrieve the device from the circuit using Circuit::device_by_id. + * When assigned, the device ID is not 0. + */ + size_t id () const + { + return m_id; + } + /** * @brief Sets the name */ @@ -655,6 +666,7 @@ private: std::string m_name; std::vector m_terminal_refs; std::vector m_parameters; + size_t m_id; /** * @brief Sets the terminal reference for a specific terminal @@ -668,6 +680,14 @@ private: { mp_device_class = dc; } + + /** + * @brief Sets the device ID + */ + void set_id (size_t id) + { + m_id = id; + } }; /** @@ -999,6 +1019,23 @@ public: */ void remove_device (Device *device); + /** + * @brief Gets the device from a given ID (const version) + * + * If the ID is not valid, null is returned. + */ + const Device *device_by_id (size_t id) const + { + return (const_cast (this)->device_by_id (id)); + } + + /** + * @brief Gets the device from a given ID (const version) + * + * If the ID is not valid, null is returned. + */ + Device *device_by_id (size_t id); + /** * @brief Begin iterator for the devices of the circuit (non-const version) */ @@ -1127,6 +1164,8 @@ private: sub_circuit_list m_sub_circuits; Netlist *mp_netlist; std::vector m_pin_refs; + bool m_valid_device_id_table; + std::map m_device_id_table; /** * @brief Sets the pin reference for a specific pin @@ -1138,6 +1177,9 @@ private: void set_netlist (Netlist *netlist); void combine_parallel_devices (const db::DeviceClass &cls); void combine_serial_devices (const db::DeviceClass &cls); + + void validate_device_id_table (); + void invalidate_device_id_table (); }; /** diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 9cee3ee5e..cd52b6ee8 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -51,6 +51,12 @@ Class decl_dbDevice ("db", "Device", gsi::method ("device_class", &db::Device::device_class, "@brief Gets the device class the device belongs to.\n" ) + + gsi::method ("id", &db::Device::id, + "@brief Gets the device ID.\n" + "The ID is a unique integer which identifies the device.\n" + "It can be used to retrieve the device from the circuit using \\Circuit#device_by_id.\n" + "When assigned, the device ID is not 0.\n" + ) + gsi::method ("name=", &db::Device::set_name, gsi::arg ("name"), "@brief Sets the name of the device.\n" "Device names are used to name a device inside a netlist file. " @@ -607,6 +613,10 @@ Class decl_dbCircuit ("db", "Circuit", gsi::iterator ("each_pin", (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::begin_pins, (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::end_pins, "@brief Iterates over the pins of the circuit" ) + + gsi::method ("device_by_id", (db::Device *(db::Circuit::*) (size_t)) &db::Circuit::device_by_id, gsi::arg ("id"), + "@brief Gets the device object for a given ID.\n" + "If the ID is not a valid device ID, nil is returned." + ) + gsi::method ("pin_by_id", &db::Circuit::pin_by_id, gsi::arg ("id"), "@brief Gets the \\Pin object corresponding to a specific ID\n" "If the ID is not a valid pin ID, nil is returned." diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index c1a5c14f4..f74ab9487 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -526,8 +526,7 @@ static std::string netlist2string (const db::Netlist &nl) res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n"; -// @@@ good for debugging -#if 0 +#if 0 // for debugging for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n"; } diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index d8cd7907c..1f7a9009e 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -264,12 +264,26 @@ TEST(4_CircuitDevices) "c:\n" ); + db::Device *dd = new db::Device (&dc1, "dd"); db::Device *d1 = new db::Device (&dc1, "d1"); db::Device *d2a = new db::Device (&dc2, "d2a"); db::Device *d2b = new db::Device (&dc2, "d2b"); c->add_device (d1); + EXPECT_EQ (d1->id (), 1); + EXPECT_EQ (c->device_by_id (d1->id ()) == d1, true); + c->add_device (dd); + EXPECT_EQ (dd->id (), 2); + EXPECT_EQ (c->device_by_id (dd->id ()) == dd, true); c->add_device (d2a); + EXPECT_EQ (d2a->id (), 3); + EXPECT_EQ (c->device_by_id (d2a->id ()) == d2a, true); c->add_device (d2b); + EXPECT_EQ (d2b->id (), 4); + EXPECT_EQ (c->device_by_id (d2b->id ()) == d2b, true); + c->remove_device (dd); + dd = 0; + EXPECT_EQ (c->device_by_id (d2a->id ()) == d2a, true); + EXPECT_EQ (c->device_by_id (2) == 0, true); EXPECT_EQ (d1->parameter_value (0), 1.0); EXPECT_EQ (d1->parameter_value (1), 2.0); diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 5d9d503a9..aad2539e3 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -157,6 +157,9 @@ class DBNetlist_TestClass < TestBase d1 = c.create_device(dc) d1.name = "D1" assert_equal(d1.name, "D1") + assert_equal(d1.id, 1) + assert_equal(c.device_by_id(d1.id).id, 1) + assert_equal(c.device_by_id(2).inspect, "nil") d2 = c.create_device(dc) assert_equal(d2.device_class.id, dc.id) From f5071d3254667959b94a5f75bf2697980bb2bb5a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 01:03:52 +0100 Subject: [PATCH 126/335] WIP: some more refactoring. --- .../dbNetlistDeviceExtractorTests.cc | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index f74ab9487..cb6d85a53 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -435,14 +435,10 @@ static std::string net_name (const db::Net *net) } } -// @@@ TODO: refactor. This is inefficient. Give an ID automatically. -static std::string device_name (const db::Device &device, const db::Circuit &circuit) +static std::string device_name (const db::Device &device) { if (device.name ().empty ()) { - int id = 1; - for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices () && d.operator-> () != &device; ++d, ++id) - ; - return "$" + tl::to_string (id); + return "$" + tl::to_string (device.id ()); } else { return device.name (); } @@ -461,14 +457,11 @@ static std::string subcircuit_name (const db::SubCircuit &subcircuit, const db:: } } -// @@@ TODO: refactor. This is inefficient. Give an ID automatically. -static std::string pin_name (const db::Pin &pin, const db::Circuit &circuit) +static std::string pin_name (const db::Pin &pin) { if (pin.name ().empty ()) { - int id = 1; - for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins () && p.operator-> () != &pin; ++p, ++id) - ; - return "$" + tl::to_string (id); + // the pin ID is zero-based and essentially the index, so we add 1 to make it compliant with the other IDs + return "$" + tl::to_string (pin.id () + 1); } else { return pin.name (); } @@ -521,7 +514,7 @@ static std::string netlist2string (const db::Netlist &nl) if (! ps.empty ()) { ps += ","; } - ps += pin_name (*p, *c) + "=" + net_name (c->net_for_pin (p->id ())); + ps += pin_name (*p) + "=" + net_name (c->net_for_pin (p->id ())); } res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n"; @@ -541,7 +534,7 @@ static std::string netlist2string (const db::Netlist &nl) } ts += t->name () + "=" + net_name (d->net_for_terminal (t->id ())); } - res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d, *c) + " (" + ts + ")\n"; + res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d) + " (" + ts + ")\n"; } for (db::Circuit::const_sub_circuit_iterator sc = c->begin_sub_circuits (); sc != c->end_sub_circuits (); ++sc) { @@ -552,7 +545,7 @@ static std::string netlist2string (const db::Netlist &nl) ps += ","; } const db::Pin &pin = *p; - ps += pin_name (pin, *subcircuit.circuit ()) + "=" + net_name (subcircuit.net_for_pin (pin.id ())); + ps += pin_name (pin) + "=" + net_name (subcircuit.net_for_pin (pin.id ())); } res += std::string (" X") + sc->circuit ()->name () + " " + subcircuit_name (*sc, *c) + " (" + ps + ")\n"; } From 62ffcd38e6f2103fbe5629bf429eef00bb00d5a5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 01:27:58 +0100 Subject: [PATCH 127/335] WIP: refactoring of the netlist property thing. Now it's internal and there is only the device terminal property. The property also does not store device pointers but just IDs --- src/db/db/db.pro | 1 - src/db/db/dbNetlistDeviceExtractor.cc | 2 +- src/db/db/dbNetlistProperty.cc | 105 +++------------ src/db/db/dbNetlistProperty.h | 92 ++----------- src/db/db/gsiDeclDbNetlistProperty.cc | 122 ------------------ .../dbNetlistDeviceExtractorTests.cc | 10 +- src/db/unit_tests/dbNetlistPropertyTests.cc | 56 +++----- src/rba/unit_tests/rba.cc | 1 - testdata/ruby/dbNetlistProperty.rb | 101 --------------- 9 files changed, 56 insertions(+), 434 deletions(-) delete mode 100644 src/db/db/gsiDeclDbNetlistProperty.cc delete mode 100644 testdata/ruby/dbNetlistProperty.rb diff --git a/src/db/db/db.pro b/src/db/db/db.pro index e2bc42515..47346293b 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -143,7 +143,6 @@ SOURCES = \ dbDeepRegion.cc \ dbHierNetworkProcessor.cc \ dbNetlistProperty.cc \ - gsiDeclDbNetlistProperty.cc \ dbNetlist.cc \ gsiDeclDbNetlist.cc \ dbNetlistDeviceClasses.cc \ diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index e0134c619..a63744a90 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -217,7 +217,7 @@ void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id // Build a property set for the DeviceTerminalProperty db::PropertiesRepository::properties_set ps; tl::Variant &v = ps.insert (std::make_pair (m_propname_id, tl::Variant ()))->second; - v = tl::Variant (new db::DeviceTerminalProperty (db::NetTerminalRef (device, terminal_id)), db::NetlistProperty::variant_class (), true); + v = tl::Variant (new db::DeviceTerminalProperty (device->id (), terminal_id), db::NetlistProperty::variant_class (), true); db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); db::PolygonRef pr (polygon, mp_layout->shape_repository ()); diff --git a/src/db/db/dbNetlistProperty.cc b/src/db/db/dbNetlistProperty.cc index cf33bfab3..c34f67407 100644 --- a/src/db/db/dbNetlistProperty.cc +++ b/src/db/db/dbNetlistProperty.cc @@ -123,111 +123,47 @@ const tl::VariantUserClass *NetlistProperty::variant_class return &s_cls; } -// -------------------------------------------------------------------------------------------- -// NetNameProperty Implementation - -NetNameProperty::NetNameProperty () - : NetlistProperty () -{ - // .. nothing yet .. -} - -NetNameProperty::NetNameProperty (const NetNameProperty &other) - : NetlistProperty (other), m_name (other.m_name) -{ - // .. nothing yet .. -} - -NetNameProperty::NetNameProperty (const std::string &n) - : NetlistProperty (), m_name (n) -{ - // .. nothing yet .. -} - -NetNameProperty &NetNameProperty::operator= (const NetNameProperty &other) -{ - NetlistProperty::operator= (other); - if (this != &other) { - m_name = other.m_name; - } - return *this; -} - -void NetNameProperty::set_name (const std::string &n) -{ - m_name = n; -} - -bool NetNameProperty::equals (const NetlistProperty *p) const -{ - const NetNameProperty *pp = static_cast (p); - return NetlistProperty::equals (p) && m_name == pp->m_name; -} - -bool NetNameProperty::less (const NetlistProperty *p) const -{ - if (! NetlistProperty::equals (p)) { - return NetlistProperty::less (p); - } else { - const NetNameProperty *pp = static_cast (p); - return m_name < pp->m_name; - } -} - -void NetNameProperty::assign (const NetlistProperty *p) -{ - NetlistProperty::assign (p); - const NetNameProperty *pp = static_cast (p); - m_name = pp->m_name; -} - - -static const char *valid_netname_chars = "_.$[]():-,"; - -std::string NetNameProperty::to_string () const -{ - return "name:" + tl::to_word_or_quoted_string (m_name, valid_netname_chars); -} - // -------------------------------------------------------------------------------------------- // DeviceTerminalProperty Implementation DeviceTerminalProperty::DeviceTerminalProperty () - : NetlistProperty () + : NetlistProperty (), m_terminal_id (0), m_device_id (0) { // .. nothing yet .. } DeviceTerminalProperty::DeviceTerminalProperty (const DeviceTerminalProperty &other) - : NetlistProperty (other), m_terminal_ref (other.m_terminal_ref) + : NetlistProperty (other), m_terminal_id (other.m_terminal_id), m_device_id (other.m_device_id) { // .. nothing yet .. } -DeviceTerminalProperty::DeviceTerminalProperty (const db::NetTerminalRef &p) - : NetlistProperty (), m_terminal_ref (p) +DeviceTerminalProperty::DeviceTerminalProperty (size_t device_id, size_t terminal_id) + : NetlistProperty (), m_terminal_id (terminal_id), m_device_id (device_id) { // .. nothing yet .. } +void DeviceTerminalProperty::set_terminal_ref (size_t device_id, size_t terminal_id) +{ + m_device_id = device_id; + m_terminal_id = terminal_id; +} + DeviceTerminalProperty &DeviceTerminalProperty::operator= (const DeviceTerminalProperty &other) { NetlistProperty::operator= (other); if (this != &other) { - m_terminal_ref = other.m_terminal_ref; + m_terminal_id = other.m_terminal_id; + m_device_id = other.m_device_id; } return *this; } -void DeviceTerminalProperty::set_terminal_ref (const db::NetTerminalRef &p) -{ - m_terminal_ref = p; -} - bool DeviceTerminalProperty::equals (const NetlistProperty *p) const { const DeviceTerminalProperty *pp = static_cast (p); - return NetlistProperty::equals (p) && m_terminal_ref == pp->m_terminal_ref; + return NetlistProperty::equals (p) && m_terminal_id == pp->m_terminal_id && m_device_id == pp->m_device_id; } bool DeviceTerminalProperty::less (const NetlistProperty *p) const @@ -236,7 +172,11 @@ bool DeviceTerminalProperty::less (const NetlistProperty *p) const return NetlistProperty::less (p); } else { const DeviceTerminalProperty *pp = static_cast (p); - return m_terminal_ref < pp->m_terminal_ref; + if (m_terminal_id != pp->m_terminal_id) { + return m_terminal_id < pp->m_terminal_id; + } else { + return m_device_id < pp->m_device_id; + } } } @@ -244,17 +184,14 @@ void DeviceTerminalProperty::assign (const NetlistProperty *p) { NetlistProperty::assign (p); const DeviceTerminalProperty *pp = static_cast (p); - m_terminal_ref = pp->m_terminal_ref; + m_terminal_id = pp->m_terminal_id; + m_device_id = pp->m_device_id; } std::string DeviceTerminalProperty::to_string () const { - if (m_terminal_ref.device () && m_terminal_ref.terminal_def ()) { - return "terminal:" + tl::to_word_or_quoted_string (m_terminal_ref.device ()->name ()) + ":" + tl::to_word_or_quoted_string (m_terminal_ref.terminal_def ()->name ()); - } else { - return "terminal"; - } + return tl::to_string (m_device_id) + ":" + tl::to_string (m_terminal_id); } } diff --git a/src/db/db/dbNetlistProperty.h b/src/db/db/dbNetlistProperty.h index c49ec0a03..b5d4aeb2b 100644 --- a/src/db/db/dbNetlistProperty.h +++ b/src/db/db/dbNetlistProperty.h @@ -151,78 +151,6 @@ public: } }; -/** - * @brief A property meaning a net name - */ -class DB_PUBLIC NetNameProperty - : public db::NetlistProperty -{ -public: - /** - * @brief Creates a netlist name property without a specific name - */ - NetNameProperty (); - - /** - * @brief copy constructor - */ - NetNameProperty (const NetNameProperty &other); - - /** - * @brief Creates a netlist name property with the given name - */ - NetNameProperty (const std::string &n); - - /** - * @brief Assignment - */ - NetNameProperty &operator= (const NetNameProperty &other); - - /** - * @brief Sets the name - */ - void set_name (const std::string &n); - - /** - * @brief Gets the name - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief Clones the object - */ - virtual NetlistProperty *clone () const - { - return new NetNameProperty (*this); - } - - /** - * @brief Compares two objects (equal). Both types are guaranteed to be the same. - */ - virtual bool equals (const NetlistProperty *) const; - - /** - * @brief Compares two objects (less). Both types are guaranteed to be the same. - */ - virtual bool less (const NetlistProperty *) const; - - /** - * @brief Assigned the other object to self. Both types are guaranteed to be identical. - */ - virtual void assign (const NetlistProperty *); - - /** - * @brief Converts to a string - */ - virtual std::string to_string () const; - -private: - std::string m_name; -}; - /** * @brief A reference to a device terminal * @@ -248,7 +176,7 @@ public: /** * @brief Creates a netlist name property with the given name */ - DeviceTerminalProperty (const db::NetTerminalRef &terminal_ref); + DeviceTerminalProperty (size_t device_id, size_t terminal_id); /** * @brief Assignment @@ -258,14 +186,22 @@ public: /** * @brief Sets the terminal reference */ - void set_terminal_ref (const db::NetTerminalRef &terminal_ref); + void set_terminal_ref (size_t device_id, size_t terminal_id); /** - * @brief Gets the terminal reference + * @brief Gets the terminal ID */ - const db::NetTerminalRef &terminal_ref () const + size_t terminal_id () const { - return m_terminal_ref; + return m_terminal_id; + } + + /** + * @brief Gets the device ID + */ + size_t device_id () const + { + return m_device_id; } /** @@ -297,7 +233,7 @@ public: virtual std::string to_string () const; private: - db::NetTerminalRef m_terminal_ref; + size_t m_terminal_id, m_device_id; }; } diff --git a/src/db/db/gsiDeclDbNetlistProperty.cc b/src/db/db/gsiDeclDbNetlistProperty.cc deleted file mode 100644 index 8d504f6c0..000000000 --- a/src/db/db/gsiDeclDbNetlistProperty.cc +++ /dev/null @@ -1,122 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2018 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 "gsiDecl.h" -#include "dbNetlistProperty.h" - -namespace gsi -{ - -// --------------------------------------------------------------- -// db::NetlistProperty binding - -static db::NetlistProperty *new_property () -{ - return new db::NetlistProperty (); -} - -gsi::Class decl_NetlistProperty ("db", "NetlistProperty", - gsi::constructor ("new", &new_property, - "@brief Creates a plain netlist property" - ) + - gsi::method ("to_s", &db::NetlistProperty::to_string, - "@brief Convert the property to a string.\n" - "@return The string representing this property\n" - ), - "@brief A generic base class for netlist properties.\n" - "\n" - "Netlist properties are used to annotate shapes or other objects with net properties. " - "Netlist properties are net names or device terminals. " - "Netlist properties can be stored inside property sets. " - "This class provides the base class for such netlist properties." - "\n\n" - "This class was introduced in version 0.26.\n" -); - -// --------------------------------------------------------------- -// db::NetNameProperty binding - -static db::NetNameProperty *new_netname () -{ - return new db::NetNameProperty (); -} - -static db::NetNameProperty *new_netname2 (const std::string &n) -{ - return new db::NetNameProperty (n); -} - -gsi::Class decl_NetNameProperty (decl_NetlistProperty, "db", "NetNameProperty", - gsi::constructor ("new", &new_netname, - "@brief Creates a new net name property object without a specific name" - ) + - gsi::constructor ("new", &new_netname2, gsi::arg ("name"), - "@brief Creates a new net name property object with the given name" - ) + - gsi::method ("name=", &db::NetNameProperty::set_name, gsi::arg ("n"), - "@brief Sets the name\n" - ) + - gsi::method ("name", &db::NetNameProperty::name, - "@brief Gets the name\n" - ), - "@brief A net name property.\n" - "\n" - "The netlist property annotates a shape or other object with a net name." - "\n\n" - "This class was introduced in version 0.26.\n" -); - -// --------------------------------------------------------------- -// db::DeviceTerminalProperty binding - -static db::DeviceTerminalProperty *new_devterminal () -{ - return new db::DeviceTerminalProperty (); -} - -static db::DeviceTerminalProperty *new_devterminal2 (const db::NetTerminalRef &n) -{ - return new db::DeviceTerminalProperty (n); -} - -gsi::Class decl_DeviceTerminalProperty (decl_NetlistProperty, "db", "DeviceTerminalProperty", - gsi::constructor ("new", &new_devterminal, - "@brief Creates a new device terminal property" - ) + - gsi::constructor ("new", &new_devterminal2, gsi::arg ("terminal_ref"), - "@brief Creates a new device terminal property with the given terminal reference" - ) + - gsi::method ("terminal_ref=", &db::DeviceTerminalProperty::set_terminal_ref, gsi::arg ("p"), - "@brief Sets the terminal reference\n" - ) + - gsi::method ("terminal_ref", &db::DeviceTerminalProperty::terminal_ref, - "@brief Gets the terminal reference\n" - ), - "@brief A device terminal reference property.\n" - "\n" - "The netlist property annotates a shape or other object with a reference to a device terminal." - "\n\n" - "This class was introduced in version 0.26.\n" -); - -} diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index cb6d85a53..bfa83a9e7 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -370,9 +370,6 @@ public: const local_cluster_type &lc = clusters.cluster_by_id (*c); for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { - // @@@ TODO: needs refactoring!!! - // -> use two distinct and reserved property name ID's for names (=string) and device terminal refs (=single number) instead - // of the scary DeviceTerminalProperty (pointer!!!) const db::PropertiesRepository::properties_set &ps = layout->properties_repository ().properties (*a); for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { @@ -381,11 +378,10 @@ public: if (j->second.is_user ()) { const db::NetlistProperty *np = &j->second.to_user (); const db::DeviceTerminalProperty *tp = dynamic_cast (np); - const db::NetNameProperty *nnp = dynamic_cast (np); if (tp) { - const_cast (tp->terminal_ref ().device ())->connect_terminal (tp->terminal_ref ().terminal_id (), net); - } else if (nnp) { - net->set_name (nnp->name ()); + db::Device *device = circuit->device_by_id (tp->device_id ()); + tl_assert (device != 0); + device->connect_terminal (tp->terminal_id (), net); } } diff --git a/src/db/unit_tests/dbNetlistPropertyTests.cc b/src/db/unit_tests/dbNetlistPropertyTests.cc index 420ebf1c9..e477d15e5 100644 --- a/src/db/unit_tests/dbNetlistPropertyTests.cc +++ b/src/db/unit_tests/dbNetlistPropertyTests.cc @@ -28,58 +28,36 @@ #include -TEST(1_NameBasic) +TEST(1_TerminalRefBasic) { - db::NetNameProperty name; - EXPECT_EQ (name.to_string (), "name:''"); + db::DeviceTerminalProperty dp (42, 17); + EXPECT_EQ (dp.to_string (), "42:17"); + EXPECT_EQ (dp.device_id () == 42, true); + EXPECT_EQ (dp.terminal_id () == 17, true); - name.set_name ("abc"); - EXPECT_EQ (name.to_string (), "name:abc"); - EXPECT_EQ (name.name (), "abc"); - - db::NetNameProperty n2 = name; - EXPECT_EQ (n2.name (), "abc"); - - n2 = db::NetNameProperty ("xyz"); - EXPECT_EQ (n2.name (), "xyz"); - - n2.set_name ("\"quoted\""); - EXPECT_EQ (n2.to_string (), "name:'\"quoted\"'"); -} - -TEST(2_TerminalRefBasic) -{ - db::DeviceClass dc; - dc.add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); - dc.add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B")); - - db::Device d (&dc, "D"); - - db::DeviceTerminalProperty dp (db::NetTerminalRef (&d, 1)); - EXPECT_EQ (dp.to_string (), "terminal:D:B"); - - dp.set_terminal_ref (db::NetTerminalRef (&d, 0)); - EXPECT_EQ (dp.to_string (), "terminal:D:A"); - EXPECT_EQ (dp.terminal_ref () == db::NetTerminalRef (&d, 0), true); + dp.set_terminal_ref (2, 1); + EXPECT_EQ (dp.to_string (), "2:1"); + EXPECT_EQ (dp.device_id () == 2, true); + EXPECT_EQ (dp.terminal_id () == 1, true); db::DeviceTerminalProperty dp2 = dp; - EXPECT_EQ (dp2.to_string (), "terminal:D:A"); + EXPECT_EQ (dp2.to_string (), "2:1"); } -TEST(3_Variants) +TEST(2_Variants) { - std::auto_ptr nn (new db::NetNameProperty ()); - nn->set_name ("net42"); + std::auto_ptr dp (new db::DeviceTerminalProperty ()); + dp->set_terminal_ref (42, 17); - tl::Variant v (nn.release (), db::NetlistProperty::variant_class (), true); + tl::Variant v (dp.release (), db::NetlistProperty::variant_class (), true); EXPECT_EQ (v.is_user (), true); - EXPECT_EQ (dynamic_cast(v.to_user ()).name (), "net42"); - EXPECT_EQ (v.to_string (), "name:net42"); + EXPECT_EQ (dynamic_cast(v.to_user ()).to_string (), "42:17"); + EXPECT_EQ (v.to_string (), "42:17"); tl::Variant vv = v; v = tl::Variant (); EXPECT_EQ (v.is_user (), false); EXPECT_EQ (vv.is_user (), true); - EXPECT_EQ (dynamic_cast(vv.to_user ()).name (), "net42"); + EXPECT_EQ (dynamic_cast(vv.to_user ()).to_string (), "42:17"); } diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index 9b7f1310c..51020ae1e 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -109,7 +109,6 @@ RUBYTEST (dbLayoutTest, "dbLayoutTest.rb") RUBYTEST (dbLayoutDiff, "dbLayoutDiff.rb") RUBYTEST (dbLayoutQuery, "dbLayoutQuery.rb") RUBYTEST (dbMatrix, "dbMatrix.rb") -RUBYTEST (dbNetlistProperty, "dbNetlistProperty.rb") RUBYTEST (dbNetlist, "dbNetlist.rb") RUBYTEST (dbPathTest, "dbPathTest.rb") RUBYTEST (dbPCells, "dbPCells.rb") diff --git a/testdata/ruby/dbNetlistProperty.rb b/testdata/ruby/dbNetlistProperty.rb deleted file mode 100644 index 3b1075485..000000000 --- a/testdata/ruby/dbNetlistProperty.rb +++ /dev/null @@ -1,101 +0,0 @@ -# encoding: UTF-8 - -# KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -if !$:.member?(File::dirname($0)) - $:.push(File::dirname($0)) -end - -load("test_prologue.rb") - -class DBNetlistProperty_TestClass < TestBase - - def test_1_Basic - - np = RBA::NetlistProperty::new - assert_equal(np.is_a?(RBA::NetlistProperty), true) - assert_equal(np.to_s, "") - - end - - def test_2_NetName - - np = RBA::NetNameProperty::new - assert_equal(np.is_a?(RBA::NetlistProperty), true) - assert_equal(np.is_a?(RBA::NetNameProperty), true) - assert_equal(np.to_s, "name:''") - - np.name = "abc" - assert_equal(np.to_s, "name:abc") - assert_equal(np.name, "abc") - - end - - def test_3_DeviceTerminal - - np = RBA::DeviceTerminalProperty::new - assert_equal(np.is_a?(RBA::DeviceTerminalProperty), true) - assert_equal(np.is_a?(RBA::DeviceTerminalProperty), true) - assert_equal(np.to_s, "terminal") - - dc = RBA::GenericDeviceClass::new - dp = RBA::DeviceTerminalDefinition::new - dp.name = "A" - dc.add_terminal(dp) - dp.name = "B" - dc.add_terminal(dp) - - c = RBA::Circuit::new - d = c.create_device(dc, "D") - - n = c.create_net("NET") - d.connect_terminal(0, n) - - # there is no other way to produce a NetTerminalRef object yet - n.each_terminal { |p| np.terminal_ref = p } - - assert_equal(np.to_s, "terminal:D:A") - assert_equal(np.terminal_ref.device.name, "D") - - end - - def test_4_VariantBinding - - ly = RBA::Layout::new - l1 = ly.insert_layer(RBA::LayerInfo::new(1, 0)) - tc = ly.create_cell("TOP") - shapes = tc.shapes(l1) - - box = shapes.insert(RBA::Box::new(0, 0, 1000, 2000)) - - # dry run - box.set_property("a", 17) - assert_equal(box.property("a"), 17) - - # set a NetNameProperty: - box.set_property("nn", RBA::NetNameProperty::new("net42")) - nn = box.property("nn") - assert_equal(nn.is_a?(RBA::NetlistProperty), true) - assert_equal(nn.is_a?(RBA::NetNameProperty), true) - assert_equal(nn.name, "net42") - - end - -end - -load("test_epilogue.rb") From 795a77f7ce1daf87fd142b1e281ecd8463893cb1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 01:39:22 +0100 Subject: [PATCH 128/335] WIP: take care of the property name used for device terminal annotation. --- src/db/db/dbNetlistDeviceExtractor.cc | 10 ++++++++-- src/db/db/dbNetlistDeviceExtractor.h | 8 ++++++++ .../unit_tests/dbNetlistDeviceExtractorTests.cc | 16 ++++++++-------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index a63744a90..60b00f006 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -41,6 +41,12 @@ NetlistDeviceExtractor::~NetlistDeviceExtractor () // .. nothing yet .. } +const tl::Variant &NetlistDeviceExtractor::terminal_property_name () +{ + static tl::Variant name ("TERMINAL"); + return name; +} + void NetlistDeviceExtractor::initialize (db::Netlist *nl) { m_device_classes.clear (); @@ -105,8 +111,8 @@ void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const mp_layout = &layout; m_layers = layers; - // terminal properties are kept in property index 0 - m_propname_id = mp_layout->properties_repository ().prop_name_id (tl::Variant (int (0))); + // terminal properties are kept in a property with the terminal_property_name name + m_propname_id = mp_layout->properties_repository ().prop_name_id (terminal_property_name ()); tl_assert (m_netlist.get () != 0); diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index e98ab6def..ca5d8563b 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -55,6 +55,14 @@ public: // TODO: Do we need to declare input layers? + /** + * @brief Gets the property name for the device terminal annotation + * Annotation happens through db::DeviceTerminalProperty objects attached to + * the terminal shapes. + * The name used for the property is the one returned by this function. + */ + static const tl::Variant &terminal_property_name (); + /** * @brief Initializes the extractor * This method will produce the device classes required for the device extraction. diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index bfa83a9e7..d0cb30f19 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -255,8 +255,6 @@ public: void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl) { - tl::Variant terminal_property_name (0); // @@@ take somewhere else - // only works for singular-layout stores currently. This rules out layers from different sources // and clipping. tl_assert (dss.layouts () == 1); @@ -265,17 +263,19 @@ public: tl_assert (layout->cells () != 0); const db::Cell &cell = layout->cell (*layout->begin_top_down ()); - // gets the text annotation property ID + // gets the text annotation property ID - + // this is how the texts are passed for annotating the net names std::pair text_annot_name_id (false, 0); if (! dss.text_property_name ().is_nil ()) { text_annot_name_id = layout->properties_repository ().get_id_of_name (dss.text_property_name ()); } - // gets the device terminal annotation property ID - std::pair terminal_annot_name_id (false, 0); - if (! terminal_property_name.is_nil ()) { - terminal_annot_name_id = layout->properties_repository ().get_id_of_name (terminal_property_name); - } + // gets the device terminal annotation property ID - + // this is how the device extractor conveys terminal shape annotations. + std::pair terminal_annot_name_id; + terminal_annot_name_id = layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_property_name ()); + + // the big part: actually extract the nets m_net_clusters.build (*layout, cell, db::ShapeIterator::Polygons, conn); From f0f620b1cdb4f8b398c3cc2ad0cbccd6d5829d95 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 01:58:34 +0100 Subject: [PATCH 129/335] WIP: added subcircuit IDs for easier referencing. --- src/db/db/dbNetlist.cc | 48 +++++++++++++++++-- src/db/db/dbNetlist.h | 41 ++++++++++++++++ src/db/db/gsiDeclDbNetlist.cc | 10 ++++ .../dbNetlistDeviceExtractorTests.cc | 15 +++--- src/db/unit_tests/dbNetlistTests.cc | 12 +++-- testdata/ruby/dbNetlist.rb | 2 + 6 files changed, 112 insertions(+), 16 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index f396b50d7..6e6963f7b 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -176,6 +176,7 @@ void Device::set_parameter_value (const std::string &name, double v) // SubCircuit class implementation SubCircuit::SubCircuit () + : m_id (0) { // .. nothing yet .. } @@ -190,12 +191,13 @@ SubCircuit::~SubCircuit() } SubCircuit::SubCircuit (Circuit *circuit, const std::string &name) - : m_circuit (circuit), m_name (name) + : m_circuit (circuit), m_name (name), m_id (0) { // .. nothing yet .. } SubCircuit::SubCircuit (const SubCircuit &other) + : m_id (0) { operator= (other); } @@ -472,13 +474,13 @@ void Net::set_circuit (Circuit *circuit) // Circuit class implementation Circuit::Circuit () - : mp_netlist (0), m_valid_device_id_table (false) + : mp_netlist (0), m_valid_device_id_table (false), m_valid_subcircuit_id_table (false) { // .. nothing yet .. } Circuit::Circuit (const Circuit &other) - : mp_netlist (0), m_valid_device_id_table (false) + : mp_netlist (0), m_valid_device_id_table (false), m_valid_subcircuit_id_table (false) { operator= (other); } @@ -489,6 +491,7 @@ Circuit &Circuit::operator= (const Circuit &other) m_name = other.m_name; invalidate_device_id_table (); + invalidate_subcircuit_id_table (); for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { add_pin (*i); @@ -560,6 +563,10 @@ void Circuit::clear () m_devices.clear (); m_nets.clear (); m_sub_circuits.clear (); + m_device_id_table.clear (); + m_subcircuit_id_table.clear (); + m_valid_device_id_table = false; + m_valid_subcircuit_id_table = false; } void Circuit::set_name (const std::string &name) @@ -637,12 +644,47 @@ Device *Circuit::device_by_id (size_t id) void Circuit::add_sub_circuit (SubCircuit *sub_circuit) { + size_t id = 0; + if (! m_sub_circuits.empty ()) { + tl_assert (m_sub_circuits.back () != 0); + id = m_sub_circuits.back ()->id (); + } + sub_circuit->set_id (id + 1); + m_sub_circuits.push_back (sub_circuit); + invalidate_subcircuit_id_table (); } void Circuit::remove_sub_circuit (SubCircuit *sub_circuit) { m_sub_circuits.erase (sub_circuit); + invalidate_subcircuit_id_table (); +} + +void Circuit::validate_subcircuit_id_table () +{ + m_subcircuit_id_table.clear (); + for (sub_circuit_iterator d = begin_sub_circuits (); d != end_sub_circuits (); ++d) { + m_subcircuit_id_table.insert (std::make_pair (d->id (), d.operator-> ())); + } + + m_valid_subcircuit_id_table = true; +} + +void Circuit::invalidate_subcircuit_id_table () +{ + m_valid_subcircuit_id_table = false; + m_subcircuit_id_table.clear (); +} + +SubCircuit *Circuit::subcircuit_by_id (size_t id) +{ + if (! m_valid_subcircuit_id_table) { + validate_subcircuit_id_table (); + } + + std::map::const_iterator d = m_subcircuit_id_table.find (id); + return d != m_subcircuit_id_table.end () ? d->second : 0; } void Circuit::translate_circuits (const std::map &map) diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index e9ceec255..39523f936 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -726,6 +726,17 @@ public: */ ~SubCircuit (); + /** + * @brief Gets the subcircuit ID + * The ID is a unique integer which identifies the subcircuit. + * It can be used to retrieve the subcircuit from the circuit using Circuit::subcircuit_by_id. + * When assigned, the subcircuit ID is not 0. + */ + size_t id () const + { + return m_id; + } + /** * @brief Gets the circuit the reference points to (const version) */ @@ -805,6 +816,7 @@ private: std::string m_name; db::DCplxTrans m_trans; std::vector m_pin_refs; + size_t m_id; /** * @brief Sets the pin reference for a specific pin @@ -818,6 +830,14 @@ private: { m_circuit.reset (c); } + + /** + * @brief Sets the device ID + */ + void set_id (size_t id) + { + m_id = id; + } }; /** @@ -1080,6 +1100,23 @@ public: */ void remove_sub_circuit (SubCircuit *sub_circuit); + /** + * @brief Gets the subcircuit from a given ID (const version) + * + * If the ID is not valid, null is returned. + */ + const SubCircuit *subcircuit_by_id (size_t id) const + { + return (const_cast (this)->subcircuit_by_id (id)); + } + + /** + * @brief Gets the subcircuit from a given ID (const version) + * + * If the ID is not valid, null is returned. + */ + SubCircuit *subcircuit_by_id (size_t id); + /** * @brief Begin iterator for the subcircuits of the circuit (non-const version) */ @@ -1166,6 +1203,8 @@ private: std::vector m_pin_refs; bool m_valid_device_id_table; std::map m_device_id_table; + bool m_valid_subcircuit_id_table; + std::map m_subcircuit_id_table; /** * @brief Sets the pin reference for a specific pin @@ -1180,6 +1219,8 @@ private: void validate_device_id_table (); void invalidate_device_id_table (); + void validate_subcircuit_id_table (); + void invalidate_subcircuit_id_table (); }; /** diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index cd52b6ee8..d647b9cae 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -128,6 +128,12 @@ Class decl_dbSubCircuit ("db", "SubCircuit", gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit, "@brief Gets the circuit referenced by the subcircuit.\n" ) + + gsi::method ("id", &db::SubCircuit::id, + "@brief Gets the subcircuit ID.\n" + "The ID is a unique integer which identifies the subcircuit.\n" + "It can be used to retrieve the subcircuit from the circuit using \\Circuit#subcircuit_by_id.\n" + "When assigned, the subcircuit ID is not 0.\n" + ) + gsi::method ("name=", &db::SubCircuit::set_name, gsi::arg ("name"), "@brief Sets the name of the subcircuit.\n" "SubCircuit names are used to name a subcircuits inside a netlist file. " @@ -617,6 +623,10 @@ Class decl_dbCircuit ("db", "Circuit", "@brief Gets the device object for a given ID.\n" "If the ID is not a valid device ID, nil is returned." ) + + gsi::method ("subcircuit_by_id", (db::SubCircuit *(db::Circuit::*) (size_t)) &db::Circuit::subcircuit_by_id, gsi::arg ("id"), + "@brief Gets the subcircuit object for a given ID.\n" + "If the ID is not a valid subcircuit ID, nil is returned." + ) + gsi::method ("pin_by_id", &db::Circuit::pin_by_id, gsi::arg ("id"), "@brief Gets the \\Pin object corresponding to a specific ID\n" "If the ID is not a valid pin ID, nil is returned." diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index d0cb30f19..33dd87329 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -279,10 +279,12 @@ public: m_net_clusters.build (*layout, cell, db::ShapeIterator::Polygons, conn); + // reverse lookup for Circuit vs. cell index std::map circuits; + // some circuits may be there because of device extraction for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { - // @@@ TODO: what if the circuits don't have a cell index? + tl_assert (layout->is_valid_cell_index (c->cell_index ())); circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); } @@ -345,7 +347,6 @@ public: std::map::const_iterator k = circuits.find (ccid); tl_assert (k != circuits.end ()); // because we walk bottom-up - // @@@ name? subcircuit = new db::SubCircuit (k->second); db::CplxTrans dbu_trans (layout->dbu ()); subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); @@ -440,14 +441,10 @@ static std::string device_name (const db::Device &device) } } -// @@@ TODO: refactor. This is inefficient. Give an ID automatically. -static std::string subcircuit_name (const db::SubCircuit &subcircuit, const db::Circuit &circuit) +static std::string subcircuit_name (const db::SubCircuit &subcircuit) { if (subcircuit.name ().empty ()) { - int id = 1; - for (db::Circuit::const_sub_circuit_iterator d = circuit.begin_sub_circuits (); d != circuit.end_sub_circuits () && d.operator-> () != &subcircuit; ++d, ++id) - ; - return "$" + tl::to_string (id); + return "$" + tl::to_string (subcircuit.id ()); } else { return subcircuit.name (); } @@ -543,7 +540,7 @@ static std::string netlist2string (const db::Netlist &nl) const db::Pin &pin = *p; ps += pin_name (pin) + "=" + net_name (subcircuit.net_for_pin (pin.id ())); } - res += std::string (" X") + sc->circuit ()->name () + " " + subcircuit_name (*sc, *c) + " (" + ps + ")\n"; + res += std::string (" X") + sc->circuit ()->name () + " " + subcircuit_name (*sc) + " (" + ps + ")\n"; } } diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 1f7a9009e..ac4c95ac0 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -269,16 +269,16 @@ TEST(4_CircuitDevices) db::Device *d2a = new db::Device (&dc2, "d2a"); db::Device *d2b = new db::Device (&dc2, "d2b"); c->add_device (d1); - EXPECT_EQ (d1->id (), 1); + EXPECT_EQ (d1->id (), size_t (1)); EXPECT_EQ (c->device_by_id (d1->id ()) == d1, true); c->add_device (dd); - EXPECT_EQ (dd->id (), 2); + EXPECT_EQ (dd->id (), size_t (2)); EXPECT_EQ (c->device_by_id (dd->id ()) == dd, true); c->add_device (d2a); - EXPECT_EQ (d2a->id (), 3); + EXPECT_EQ (d2a->id (), size_t (3)); EXPECT_EQ (c->device_by_id (d2a->id ()) == d2a, true); c->add_device (d2b); - EXPECT_EQ (d2b->id (), 4); + EXPECT_EQ (d2b->id (), size_t (4)); EXPECT_EQ (c->device_by_id (d2b->id ()) == d2b, true); c->remove_device (dd); dd = 0; @@ -416,10 +416,14 @@ TEST(4_NetlistSubcircuits) db::SubCircuit *sc1 = new db::SubCircuit (c2); sc1->set_name ("sc1"); c1->add_sub_circuit (sc1); + EXPECT_EQ (sc1->id (), size_t (1)); + EXPECT_EQ (c1->subcircuit_by_id (sc1->id ()) == sc1, true); db::SubCircuit *sc2 = new db::SubCircuit (c2); sc2->set_name ("sc2"); c1->add_sub_circuit (sc2); + EXPECT_EQ (sc2->id (), size_t (2)); + EXPECT_EQ (c1->subcircuit_by_id (sc2->id ()) == sc2, true); db::Net *n2a = new db::Net (); c2->add_net (n2a); diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index aad2539e3..bcd6314bc 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -260,6 +260,8 @@ class DBNetlist_TestClass < TestBase sc1.name = "SC1" assert_equal(sc1.name, "SC1") assert_equal(sc1.circuit.name, "CC") + assert_equal(c.subcircuit_by_id(sc1.id).id, 1) + assert_equal(c.subcircuit_by_id(2).inspect, "nil") sc2 = c.create_subcircuit(cc) sc2.name = "SC2" From bfae347ffba3e74ed5eca77c0e6903acafc201de Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 02:02:17 +0100 Subject: [PATCH 130/335] WIP: renaming sub_circuit->subcircuit for consistency --- src/db/db/dbNetlist.cc | 26 ++++++++--------- src/db/db/dbNetlist.h | 28 +++++++++---------- src/db/db/gsiDeclDbNetlist.cc | 6 ++-- .../dbNetlistDeviceExtractorTests.cc | 4 +-- src/db/unit_tests/dbNetlistTests.cc | 14 +++++----- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 6e6963f7b..ec2a987bd 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -505,10 +505,10 @@ Circuit &Circuit::operator= (const Circuit &other) } std::map sc_table; - for (const_sub_circuit_iterator i = other.begin_sub_circuits (); i != other.end_sub_circuits (); ++i) { + for (const_subcircuit_iterator i = other.begin_subcircuits (); i != other.end_subcircuits (); ++i) { SubCircuit *sc = new SubCircuit (*i); sc_table [i.operator-> ()] = sc; - add_sub_circuit (sc); + add_subcircuit (sc); } for (const_net_iterator i = other.begin_nets (); i != other.end_nets (); ++i) { @@ -562,7 +562,7 @@ void Circuit::clear () m_pins.clear (); m_devices.clear (); m_nets.clear (); - m_sub_circuits.clear (); + m_subcircuits.clear (); m_device_id_table.clear (); m_subcircuit_id_table.clear (); m_valid_device_id_table = false; @@ -642,29 +642,29 @@ Device *Circuit::device_by_id (size_t id) return d != m_device_id_table.end () ? d->second : 0; } -void Circuit::add_sub_circuit (SubCircuit *sub_circuit) +void Circuit::add_subcircuit (SubCircuit *subcircuit) { size_t id = 0; - if (! m_sub_circuits.empty ()) { - tl_assert (m_sub_circuits.back () != 0); - id = m_sub_circuits.back ()->id (); + if (! m_subcircuits.empty ()) { + tl_assert (m_subcircuits.back () != 0); + id = m_subcircuits.back ()->id (); } - sub_circuit->set_id (id + 1); + subcircuit->set_id (id + 1); - m_sub_circuits.push_back (sub_circuit); + m_subcircuits.push_back (subcircuit); invalidate_subcircuit_id_table (); } -void Circuit::remove_sub_circuit (SubCircuit *sub_circuit) +void Circuit::remove_subcircuit (SubCircuit *subcircuit) { - m_sub_circuits.erase (sub_circuit); + m_subcircuits.erase (subcircuit); invalidate_subcircuit_id_table (); } void Circuit::validate_subcircuit_id_table () { m_subcircuit_id_table.clear (); - for (sub_circuit_iterator d = begin_sub_circuits (); d != end_sub_circuits (); ++d) { + for (subcircuit_iterator d = begin_subcircuits (); d != end_subcircuits (); ++d) { m_subcircuit_id_table.insert (std::make_pair (d->id (), d.operator-> ())); } @@ -689,7 +689,7 @@ SubCircuit *Circuit::subcircuit_by_id (size_t id) void Circuit::translate_circuits (const std::map &map) { - for (sub_circuit_iterator i = m_sub_circuits.begin (); i != m_sub_circuits.end (); ++i) { + for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) { std::map::const_iterator m = map.find (i->circuit ()); tl_assert (m != map.end ()); i->set_circuit (m->second); diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 39523f936..09652cb08 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -859,9 +859,9 @@ public: typedef tl::shared_collection net_list; typedef net_list::const_iterator const_net_iterator; typedef net_list::iterator net_iterator; - typedef tl::shared_collection sub_circuit_list; - typedef sub_circuit_list::const_iterator const_sub_circuit_iterator; - typedef sub_circuit_list::iterator sub_circuit_iterator; + typedef tl::shared_collection subcircuit_list; + typedef subcircuit_list::const_iterator const_subcircuit_iterator; + typedef subcircuit_list::iterator subcircuit_iterator; /** * @brief Constructor @@ -1093,12 +1093,12 @@ public: * * The circuit takes over ownership of the object. */ - void add_sub_circuit (SubCircuit *sub_circuit); + void add_subcircuit (SubCircuit *subcircuit); /** * @brief Deletes a subcircuit from the circuit */ - void remove_sub_circuit (SubCircuit *sub_circuit); + void remove_subcircuit (SubCircuit *subcircuit); /** * @brief Gets the subcircuit from a given ID (const version) @@ -1120,33 +1120,33 @@ public: /** * @brief Begin iterator for the subcircuits of the circuit (non-const version) */ - sub_circuit_iterator begin_sub_circuits () + subcircuit_iterator begin_subcircuits () { - return m_sub_circuits.begin (); + return m_subcircuits.begin (); } /** * @brief End iterator for the subcircuits of the circuit (non-const version) */ - sub_circuit_iterator end_sub_circuits () + subcircuit_iterator end_subcircuits () { - return m_sub_circuits.end (); + return m_subcircuits.end (); } /** * @brief Begin iterator for the subcircuits of the circuit (const version) */ - const_sub_circuit_iterator begin_sub_circuits () const + const_subcircuit_iterator begin_subcircuits () const { - return m_sub_circuits.begin (); + return m_subcircuits.begin (); } /** * @brief End iterator for the subcircuits of the circuit (const version) */ - const_sub_circuit_iterator end_sub_circuits () const + const_subcircuit_iterator end_subcircuits () const { - return m_sub_circuits.end (); + return m_subcircuits.end (); } /** @@ -1198,7 +1198,7 @@ private: net_list m_nets; pin_list m_pins; device_list m_devices; - sub_circuit_list m_sub_circuits; + subcircuit_list m_subcircuits; Netlist *mp_netlist; std::vector m_pin_refs; bool m_valid_device_id_table; diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index d647b9cae..e210eccb2 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -581,7 +581,7 @@ static db::Device *create_device1 (db::Circuit *c, db::DeviceClass *dc, const st static db::SubCircuit *create_subcircuit1 (db::Circuit *c, db::Circuit *cc, const std::string &name) { db::SubCircuit *sc = new db::SubCircuit (cc, name); - c->add_sub_circuit (sc); + c->add_subcircuit (sc); return sc; } @@ -671,10 +671,10 @@ Class decl_dbCircuit ("db", "Circuit", "\n" "For more details see the \\SubCircuit class." ) + - gsi::method ("remove_subcircuit", &db::Circuit::remove_sub_circuit, gsi::arg ("subcircuit"), + gsi::method ("remove_subcircuit", &db::Circuit::remove_subcircuit, gsi::arg ("subcircuit"), "@brief Removes the given subcircuit from the circuit\n" ) + - gsi::iterator ("each_subcircuit", (db::Circuit::sub_circuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_sub_circuits, (db::Circuit::sub_circuit_iterator (db::Circuit::*) ()) &db::Circuit::end_sub_circuits, + gsi::iterator ("each_subcircuit", (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_subcircuits, (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::end_subcircuits, "@brief Iterates over the subcircuits of the circuit" ) + gsi::method ("netlist", (db::Netlist *(db::Circuit::*) ()) &db::Circuit::netlist, diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index 33dd87329..30d0e53d6 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -350,7 +350,7 @@ public: subcircuit = new db::SubCircuit (k->second); db::CplxTrans dbu_trans (layout->dbu ()); subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); - circuit->add_sub_circuit (subcircuit); + circuit->add_subcircuit (subcircuit); subcircuits.insert (std::make_pair (i->inst (), subcircuit)); } else { @@ -530,7 +530,7 @@ static std::string netlist2string (const db::Netlist &nl) res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d) + " (" + ts + ")\n"; } - for (db::Circuit::const_sub_circuit_iterator sc = c->begin_sub_circuits (); sc != c->end_sub_circuits (); ++sc) { + for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { std::string ps; const db::SubCircuit &subcircuit = *sc; for (db::Circuit::const_pin_iterator p = sc->circuit ()->begin_pins (); p != sc->circuit ()->end_pins (); ++p) { diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index ac4c95ac0..138e4dd41 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -220,7 +220,7 @@ static std::string netlist2 (const db::Circuit &c) res += " D" + d->name () + ":" + pins + "\n"; } - for (db::Circuit::const_sub_circuit_iterator s = c.begin_sub_circuits (); s != c.end_sub_circuits (); ++s) { + for (db::Circuit::const_subcircuit_iterator s = c.begin_subcircuits (); s != c.end_subcircuits (); ++s) { if (! s->circuit ()) { continue; } @@ -415,13 +415,13 @@ TEST(4_NetlistSubcircuits) db::SubCircuit *sc1 = new db::SubCircuit (c2); sc1->set_name ("sc1"); - c1->add_sub_circuit (sc1); + c1->add_subcircuit (sc1); EXPECT_EQ (sc1->id (), size_t (1)); EXPECT_EQ (c1->subcircuit_by_id (sc1->id ()) == sc1, true); db::SubCircuit *sc2 = new db::SubCircuit (c2); sc2->set_name ("sc2"); - c1->add_sub_circuit (sc2); + c1->add_subcircuit (sc2); EXPECT_EQ (sc2->id (), size_t (2)); EXPECT_EQ (c1->subcircuit_by_id (sc2->id ()) == sc2, true); @@ -656,10 +656,10 @@ TEST(8_NetSubCircuitsEditing) cc2.add_pin (db::Pin ("B")); db::SubCircuit *sc1 = new db::SubCircuit (&cc1, "sc1"); - c.add_sub_circuit (sc1); + c.add_subcircuit (sc1); db::SubCircuit *sc2 = new db::SubCircuit (&cc2, "sc2"); - c.add_sub_circuit (sc2); + c.add_subcircuit (sc2); db::Net *n1 = new db::Net (); n1->set_name ("n1"); @@ -722,8 +722,8 @@ TEST(8_NetSubCircuitsEditing) delete sc1; sc1 = 0; - EXPECT_EQ (c.begin_sub_circuits ()->name (), "sc2"); - EXPECT_EQ (++c.begin_sub_circuits () == c.end_sub_circuits (), true); + EXPECT_EQ (c.begin_subcircuits ()->name (), "sc2"); + EXPECT_EQ (++c.begin_subcircuits () == c.end_subcircuits (), true); EXPECT_EQ (net2string (*n1), "sc2:B"); EXPECT_EQ (net2string (*n2), "sc2:A"); From 639b0026d36ce2f7b1126b8ad751ab5aed32952f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 10:33:08 +0100 Subject: [PATCH 131/335] WIP: some refactoring for better consistency and easier usage of the device extractor. --- src/db/db/dbDeepRegion.cc | 28 ++--- src/db/db/dbDeepShapeStore.cc | 78 ++++++++++---- src/db/db/dbDeepShapeStore.h | 100 ++++++++++++++++-- src/db/db/dbHierNetworkProcessor.cc | 13 +++ src/db/db/dbHierNetworkProcessor.h | 16 +++ src/db/db/dbNetlistDeviceExtractor.cc | 45 ++------ src/db/db/dbNetlistDeviceExtractor.h | 26 ++--- src/db/unit_tests/dbDeepShapeStoreTests.cc | 22 ++-- .../dbNetlistDeviceExtractorTests.cc | 85 +++++++-------- 9 files changed, 261 insertions(+), 152 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 390301734..1b8eb3dec 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -171,15 +171,15 @@ DeepRegion::begin_merged () const std::pair DeepRegion::begin_iter () const { - const db::Layout *layout = m_deep_layer.layout (); - if (layout->cells () == 0) { + const db::Layout &layout = m_deep_layer.layout (); + if (layout.cells () == 0) { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } else { - const db::Cell &top_cell = layout->cell (*layout->begin_top_down ()); - db::RecursiveShapeIterator iter (*m_deep_layer.layout (), top_cell, m_deep_layer.layer ()); + const db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + db::RecursiveShapeIterator iter (m_deep_layer.layout (), top_cell, m_deep_layer.layer ()); return std::make_pair (iter, db::ICplxTrans ()); } @@ -236,7 +236,7 @@ bool DeepRegion::equals (const Region &other) const { const DeepRegion *other_delegate = dynamic_cast (other.delegate ()); - if (other_delegate && other_delegate->m_deep_layer.layout () == m_deep_layer.layout () + if (other_delegate && &other_delegate->m_deep_layer.layout () == &m_deep_layer.layout () && other_delegate->m_deep_layer.layer () == m_deep_layer.layer ()) { return true; } else { @@ -248,7 +248,7 @@ bool DeepRegion::less (const Region &other) const { const DeepRegion *other_delegate = dynamic_cast (other.delegate ()); - if (other_delegate && other_delegate->m_deep_layer.layout () == m_deep_layer.layout ()) { + if (other_delegate && &other_delegate->m_deep_layer.layout () == &m_deep_layer.layout ()) { return other_delegate->m_deep_layer.layer () < m_deep_layer.layer (); } else { return AsIfFlatRegion::less (other); @@ -348,7 +348,7 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const db::BoolAndOrNotLocalOperation op (and_op, m_deep_layer.store ()->max_area_ratio (), m_deep_layer.store ()->max_vertex_count ()); - db::LocalProcessor proc (const_cast (m_deep_layer.layout ()), const_cast (m_deep_layer.initial_cell ()), other->deep_layer ().layout (), other->deep_layer ().initial_cell ()); + db::LocalProcessor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ()); proc.set_threads (m_deep_layer.store ()->threads ()); proc.run (&op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); @@ -392,20 +392,20 @@ DeepRegion::xor_with (const Region &other) const void DeepRegion::add_from (const DeepLayer &dl) { - if (dl.layout () == deep_layer ().layout ()) { + if (&dl.layout () == &deep_layer ().layout ()) { // intra-layout merge - deep_layer ().layout ()->copy_layer (dl.layer (), deep_layer ().layer ()); + deep_layer ().layout ().copy_layer (dl.layer (), deep_layer ().layer ()); } else { // inter-layout merge - db::cell_index_type into_cell = deep_layer ().initial_cell ()->cell_index (); - db::Layout *into_layout = deep_layer ().layout (); - db::cell_index_type source_cell = dl.initial_cell ()->cell_index (); - const db::Layout *source_layout = dl.layout (); + db::cell_index_type into_cell = deep_layer ().initial_cell ().cell_index (); + db::Layout *into_layout = &deep_layer ().layout (); + db::cell_index_type source_cell = dl.initial_cell ().cell_index (); + const db::Layout *source_layout = &dl.layout (); db::CellMapping cm; cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell); @@ -439,7 +439,7 @@ DeepRegion::add_in_place (const Region &other) // non-deep to deep merge (flat) - db::Shapes &shapes = deep_layer ().initial_cell ()->shapes (deep_layer ().layer ()); + db::Shapes &shapes = deep_layer ().initial_cell ().shapes (deep_layer ().layer ()); for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { shapes.insert (*p); } diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index eea29866c..c0e900a17 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -24,6 +24,8 @@ #include "dbDeepShapeStore.h" #include "dbCellMapping.h" #include "dbLayoutUtils.h" +#include "dbRegion.h" +#include "dbDeepRegion.h" #include "tlTimer.h" @@ -38,6 +40,14 @@ DeepLayer::DeepLayer () // .. nothing yet .. } +DeepLayer::DeepLayer (const Region ®ion) + : mp_store (), m_layout (0), m_layer (0) +{ + const db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + *this = dr->deep_layer (); +} + DeepLayer::DeepLayer (const DeepLayer &x) : mp_store (x.mp_store), m_layout (x.m_layout), m_layer (x.m_layer) { @@ -82,7 +92,7 @@ DeepLayer::~DeepLayer () DeepLayer DeepLayer::derived () const { - return DeepLayer (const_cast (mp_store.get ()), m_layout, const_cast (layout ())->insert_layer ()); + return DeepLayer (const_cast (mp_store.get ()), m_layout, const_cast (layout ()).insert_layer ()); } DeepLayer @@ -91,9 +101,7 @@ DeepLayer::copy () const DeepLayer new_layer (derived ()); db::DeepShapeStore *non_const_store = const_cast (mp_store.get ()); - if (non_const_store->layout (m_layout)) { - non_const_store->layout (m_layout)->copy_layer (m_layer, new_layer.layer ()); - } + non_const_store->layout (m_layout).copy_layer (m_layer, new_layer.layer ()); return new_layer; } @@ -105,34 +113,32 @@ DeepLayer::insert_into (db::Layout *into_layout, db::cell_index_type into_cell, const_cast (mp_store.get ())->insert (*this, into_layout, into_cell, into_layer); } -db::Layout * +db::Layout & DeepLayer::layout () { check_dss (); return mp_store->layout (m_layout); } -const db::Layout * +const db::Layout & DeepLayer::layout () const { check_dss (); return const_cast (mp_store.get ())->layout (m_layout); } -db::Cell * +db::Cell & DeepLayer::initial_cell () { - db::Layout *ly = layout (); - tl_assert (ly->begin_top_down () != ly->end_top_down ()); - return &ly->cell (*ly->begin_top_down ()); + check_dss (); + return mp_store->initial_cell (m_layout); } -const db::Cell * +const db::Cell & DeepLayer::initial_cell () const { - const db::Layout *ly = layout (); - tl_assert (ly->begin_top_down () != ly->end_top_down ()); - return &ly->cell (*ly->begin_top_down ()); + check_dss (); + return mp_store->const_initial_cell (m_layout); } void @@ -192,6 +198,32 @@ DeepShapeStore::~DeepShapeStore () m_layouts.clear (); } +bool DeepShapeStore::is_singular () const +{ + return m_layouts.size () == 1; +} + +void DeepShapeStore::require_singular () const +{ + if (! is_singular ()) { + throw tl::Exception (tl::to_string (tr ("Internal error: deep shape store isn't singular. This may happen if you try to mix hierarchical layers from different sources our you use clipping."))); + } +} + +Cell &DeepShapeStore::initial_cell(unsigned int n) +{ + db::Layout &ly = layout (n); + tl_assert (ly.cells () > 0); + return ly.cell (*ly.begin_top_down ()); +} + +const db::Cell &DeepShapeStore::const_initial_cell (unsigned int n) const +{ + const db::Layout &ly = const_layout (n); + tl_assert (ly.cells () > 0); + return ly.cell (*ly.begin_top_down ()); +} + void DeepShapeStore::set_text_enlargement (int enl) { m_text_enlargement = enl; @@ -207,16 +239,16 @@ bool DeepShapeStore::is_valid_layout_index (unsigned int n) const return (n < (unsigned int) m_layouts.size () && m_layouts[n] != 0); } -const db::Layout *DeepShapeStore::const_layout (unsigned int n) const +const db::Layout &DeepShapeStore::const_layout (unsigned int n) const { tl_assert (is_valid_layout_index (n)); - return &(m_layouts [n]->layout); + return m_layouts [n]->layout; } -db::Layout *DeepShapeStore::layout (unsigned int n) +db::Layout &DeepShapeStore::layout (unsigned int n) { tl_assert (is_valid_layout_index (n)); - return &(m_layouts [n]->layout); + return m_layouts [n]->layout; } size_t DeepShapeStore::instance_count () @@ -395,14 +427,14 @@ DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_ void DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) { - const db::Layout *source_layout = deep_layer.layout (); - if (source_layout->begin_top_down () == source_layout->end_top_cells ()) { + const db::Layout &source_layout = deep_layer.layout (); + if (source_layout.begin_top_down () == source_layout.end_top_cells ()) { // empty source - nothing to do. return; } // prepare the transformation - db::ICplxTrans trans (source_layout->dbu () / into_layout->dbu ()); + db::ICplxTrans trans (source_layout.dbu () / into_layout->dbu ()); // prepare a layer map std::map lm; @@ -413,10 +445,10 @@ DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db // prepare a vector with the source cells std::vector source_cells; - source_cells.push_back (*source_layout->begin_top_down()); + source_cells.push_back (*source_layout.begin_top_down()); // actually copy the shapes - db::copy_shapes (*into_layout, *source_layout, trans, source_cells, cm.table (), lm); + db::copy_shapes (*into_layout, source_layout, trans, source_cells, cm.table (), lm); } } diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index d944260a9..7fcc7ec07 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -40,6 +40,7 @@ namespace db { class DeepShapeStore; +class Region; /** * @brief Represents a shape collection from the deep shape store @@ -60,6 +61,12 @@ public: */ ~DeepLayer (); + /** + * @brief Conversion operator from Region to DeepLayer + * This requires the Region to be a DeepRegion. Otherwise, this constructor will assert + */ + DeepLayer (const Region ®ion); + /** * @brief Copy constructor */ @@ -74,23 +81,23 @@ public: * @brief Gets the layout object * The return value is guaranteed to be non-null. */ - db::Layout *layout (); + Layout &layout(); /** * @brief Gets the layout object (const version) */ - const db::Layout *layout () const; + const db::Layout &layout () const; /** * @brief Gets the layout object * The return value is guaranteed to be non-null. */ - db::Cell *initial_cell (); + Cell &initial_cell(); /** * @brief Gets the initial cell object (const version) */ - const db::Cell *initial_cell () const; + const db::Cell &initial_cell () const; /** * @brief Gets the layer @@ -185,6 +192,20 @@ public: */ ~DeepShapeStore (); + /** + * @brief Returns true, if the DeepShapeStore is singular + * + * A "singular" shape store needs a single layout to keep the information. + * This is the case, if all Regions derived from it share the same origin + * and do not use clipping or region selection. Singular shape stores are + * required for netlist extraction for example. + * + * For a singular shape store, "layout()" will return the layout + * object and "initial_cell()" will return the initial cell of the + * only layout. + */ + bool is_singular () const; + /** * @brief Inserts a polygon layer into the deep shape store * @@ -219,7 +240,72 @@ public: /** * @brief Gets the nth layout (const version) */ - const db::Layout *const_layout (unsigned int n) const; + const db::Layout &const_layout (unsigned int n) const; + + /** + * @brief Gets the nth layout (non-const version) + * + * Don't try to mess too much with the layout object, you'll screw up the internals. + */ + db::Layout &layout (unsigned int n); + + /** + * @brief Gets the initial cell of the nth layout (const version) + */ + const db::Cell &const_initial_cell (unsigned int n) const; + + /** + * @brief Gets the initial cell of the nth layout (non-const version) + * + * Don't try to mess too much with the cell object, you'll screw up the internals. + */ + db::Cell &initial_cell (unsigned int n); + + /** + * @brief Gets the singular layout (const version) + * + * This method will throw an exception if the deep shape store is not singular. + */ + const db::Layout &const_layout () const + { + require_singular (); + return const_layout (0); + } + + /** + * @brief Gets the singular layout (non-const version) + * + * This method will throw an exception if the deep shape store is not singular. + * Don't try to mess too much with the layout object, you'll screw up the internals. + */ + db::Layout &layout () + { + require_singular (); + return layout (0); + } + + /** + * @brief Gets the initial cell of the singular layout (const version) + * + * This method will throw an exception if the deep shape store is not singular. + */ + const db::Cell &const_initial_cell () const + { + require_singular (); + return const_initial_cell (0); + } + + /** + * @brief Gets the initial cell of the singular layout (non-const version) + * + * This method will throw an exception if the deep shape store is not singular. + * Don't try to mess too much with the cell object, you'll screw up the internals. + */ + db::Cell &initial_cell () + { + require_singular (); + return initial_cell (0); + } /** * @brief Gets the number of layouts @@ -324,11 +410,11 @@ private: struct LayoutHolder; - db::Layout *layout (unsigned int n); - void add_ref (unsigned int layout, unsigned int layer); void remove_ref (unsigned int layout, unsigned int layer); + void require_singular () const; + typedef std::map layout_map_type; // no copying diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 6020ba4be..eadc2b973 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -28,6 +28,7 @@ #include "dbPolygon.h" #include "dbPolygonTools.h" #include "dbBoxScanner.h" +#include "dbDeepRegion.h" #include "tlProgress.h" #include "tlLog.h" #include "tlTimer.h" @@ -64,6 +65,18 @@ Connectivity::connect (unsigned int l) m_all_layers.insert (l); } +void +Connectivity::connect (const db::DeepLayer &l) +{ + connect (l.layer ()); +} + +void +Connectivity::connect (const db::DeepLayer &la, const db::DeepLayer &lb) +{ + connect (la.layer (), lb.layer ()); +} + Connectivity::layer_iterator Connectivity::begin_layers () const { diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index d27a39174..87b957f23 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -36,6 +36,8 @@ namespace db { +class DeepLayer; + /** * @brief Defines the connectivity * @@ -61,6 +63,20 @@ public: */ void connect (unsigned int la, unsigned int lb); + /** + * @brief Adds intra-layer connectivity for layer l + * This is a convenience method that takes a db::DeepLayer object. + * It is assumed that all those layers originate from the same deep shape store. + */ + void connect (const db::DeepLayer &l); + + /** + * @brief Adds inter-layer connectivity + * This is a convenience method that takes a db::DeepLayer object. + * It is assumed that all those layers originate from the same deep shape store. + */ + void connect (const db::DeepLayer &la, const db::DeepLayer &lb); + /** * @brief Adds intra-layer connectivity for layer l */ diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 60b00f006..09787dd46 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -62,49 +62,26 @@ static void insert_into_region (const db::PolygonRef &s, const db::ICplxTrans &t region.insert (s.obj ().transformed (tr * db::ICplxTrans (s.trans ()))); } -void NetlistDeviceExtractor::extract (const std::vector regions) +void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const std::vector &deep_layers, db::Netlist *nl) { - tl_assert (! regions.empty ()); + db::Layout &layout = dss.layout (); + db::Cell &cell = dss.initial_cell (); - const db::Layout *layout = 0; - const db::Cell *cell = 0; std::vector layers; - layers.reserve (regions.size ()); - - for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { - - // TODO: this is clumsy ... - db::DeepRegion *dr = dynamic_cast ((*r)->delegate ()); - tl_assert (dr != 0); - - db::DeepLayer dl = dr->deep_layer (); - tl_assert (dl.layout () != 0); - tl_assert (dl.initial_cell () != 0); - - if (! layout) { - layout = dl.layout (); - } else { - tl_assert (layout == dl.layout ()); - } - - if (! cell) { - cell = dl.initial_cell (); - } else { - tl_assert (cell == dl.initial_cell ()); - } - - layers.push_back (dl.layer ()); + layers.reserve (deep_layers.size ()); + for (std::vector::const_iterator dl = deep_layers.begin (); dl != deep_layers.end (); ++dl) { + tl_assert (&dl->layout () == &layout); + layers.push_back (dl->layer ()); } - // NOTE: the const_cast's are there because the extraction will annotate the layout with port - // shapes. That's not part of the initial design, where the underlying deep shape store is - // immutable from the outside. But we know what we're doing. - extract (const_cast (*layout), const_cast (*cell), layers); + extract (layout, cell, layers, nl); } -void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers) +void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl) { + initialize (nl); + typedef db::PolygonRef shape_type; db::ShapeIterator::flags_type shape_iter_flags = db::ShapeIterator::Polygons; diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index ca5d8563b..4f99cc1ba 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -27,6 +27,7 @@ #include "dbNetlist.h" #include "dbLayout.h" #include "dbHierNetworkProcessor.h" +#include "dbDeepShapeStore.h" #include "gsiObject.h" @@ -63,12 +64,6 @@ public: */ static const tl::Variant &terminal_property_name (); - /** - * @brief Initializes the extractor - * This method will produce the device classes required for the device extraction. - */ - void initialize (db::Netlist *nl); - /** * @brief Performs the extraction * @@ -87,21 +82,17 @@ public: * * NOTE: The extractor expects "PolygonRef" type layers. */ - void extract (Layout &layout, Cell &cell, const std::vector &layers); + void extract (Layout &layout, Cell &cell, const std::vector &layers, Netlist *netlist); /** * @brief Extracts the devices from a list of regions * * This method behaves identical to the other "extract" method, but accepts - * regions for input. - * - * As a requirement, the layout and initial cell of all of the regions - * has to be identical. - * - * Currently, the regions have to be deep regions. + * DeepShape layers for input. By definition, these already have the "PolygonRef" type. */ - void extract (const std::vector regions); + void extract (DeepShapeStore &dss, const std::vector &layers, Netlist *netlist); +protected: /** * @brief Creates the device classes * At least one device class needs to be defined. Use "register_device_class" to register @@ -131,7 +122,6 @@ public: */ virtual void extract_devices (const std::vector &layer_geometry); -protected: /** * @brief Registers a device class * The device class object will become owned by the netlist and must not be deleted by @@ -205,6 +195,12 @@ private: std::vector m_device_classes; std::vector m_layers; unsigned int m_device_name_index; + + /** + * @brief Initializes the extractor + * This method will produce the device classes required for the device extraction. + */ + void initialize (db::Netlist *nl); }; } diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc index cc3ee008d..1eb8371d5 100644 --- a/src/db/unit_tests/dbDeepShapeStoreTests.cc +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -42,12 +42,12 @@ TEST(1) EXPECT_EQ (dl1.layer (), l1); EXPECT_EQ (dl2.layer (), l2); - EXPECT_EQ (dl1.layout (), dl2.layout ()); + EXPECT_EQ (&dl1.layout (), &dl2.layout ()); EXPECT_EQ (store.layouts (), (unsigned int) 1); db::DeepLayer dl3 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c2), l1)); EXPECT_EQ (dl3.layer (), l1); - EXPECT_NE (dl1.layout (), dl3.layout ()); + EXPECT_NE (&dl1.layout (), &dl3.layout ()); EXPECT_EQ (store.layouts (), (unsigned int) 2); db::DeepLayer dl4 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1, db::Box (0, 1, 2, 3))); @@ -58,13 +58,13 @@ TEST(1) db::DeepLayer dl6 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1, db::Box (0, 1, 2, 3))); EXPECT_EQ (dl6.layer (), l2); // a new layer (a copy) - EXPECT_EQ (dl6.layout (), dl4.layout ()); + EXPECT_EQ (&dl6.layout (), &dl4.layout ()); EXPECT_EQ (store.layouts (), (unsigned int) 4); } -static size_t shapes_in_top (const db::Layout *layout, unsigned int layer) +static size_t shapes_in_top (const db::Layout &layout, unsigned int layer) { - const db::Cell &top = layout->cell (*layout->begin_top_down ()); + const db::Cell &top = layout.cell (*layout.begin_top_down ()); return top.shapes (layer).size (); } @@ -162,23 +162,23 @@ TEST(3_TextTreatment) db::DeepLayer dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); EXPECT_EQ (store.layouts (), (unsigned int) 1); - EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).empty (), true); + EXPECT_EQ (dl1.initial_cell ().shapes (dl1.layer ()).empty (), true); store.set_text_enlargement (1); dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); EXPECT_EQ (store.layouts (), (unsigned int) 1); - EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).size (), size_t (1)); - EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999)"); + EXPECT_EQ (dl1.initial_cell ().shapes (dl1.layer ()).size (), size_t (1)); + EXPECT_EQ (dl1.initial_cell ().shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999)"); store.set_text_property_name (tl::Variant ("text")); dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); EXPECT_EQ (store.layouts (), (unsigned int) 1); - EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).size (), size_t (1)); - EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999) prop_id=1"); + EXPECT_EQ (dl1.initial_cell ().shapes (dl1.layer ()).size (), size_t (1)); + EXPECT_EQ (dl1.initial_cell ().shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999) prop_id=1"); - const db::Layout *dss_layout = store.const_layout (0); + const db::Layout *dss_layout = &store.const_layout (0); db::PropertiesRepository::properties_set ps = dss_layout->properties_repository ().properties (1); EXPECT_EQ (ps.size (), size_t (1)); EXPECT_EQ (dss_layout->properties_repository ().prop_name (ps.begin ()->first).to_string (), "text"); diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index 30d0e53d6..97c648649 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -48,10 +48,9 @@ class MOSFETExtractor : public db::NetlistDeviceExtractor { public: - MOSFETExtractor (db::Netlist &nl, db::Layout *debug_out) + MOSFETExtractor (db::Layout *debug_out) : db::NetlistDeviceExtractor (), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) { - initialize (&nl); if (mp_debug_out) { m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); @@ -230,14 +229,9 @@ static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_la return lid; } -// @@@ TODO: move somewhere else static unsigned int layer_of (const db::Region ®ion) { - // TODO: this is clumsy ... - db::DeepRegion *dr = dynamic_cast (region.delegate ()); - tl_assert (dr != 0); - - return dr->deep_layer ().layer (); + return db::DeepLayer (region).layer (); } // @@@ TODO: move somewhere else @@ -255,42 +249,37 @@ public: void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl) { - // only works for singular-layout stores currently. This rules out layers from different sources - // and clipping. - tl_assert (dss.layouts () == 1); - const db::Layout *layout = dss.const_layout (0); - - tl_assert (layout->cells () != 0); - const db::Cell &cell = layout->cell (*layout->begin_top_down ()); + const db::Layout &layout = dss.const_layout (); + const db::Cell &cell = dss.const_initial_cell (); // gets the text annotation property ID - // this is how the texts are passed for annotating the net names std::pair text_annot_name_id (false, 0); if (! dss.text_property_name ().is_nil ()) { - text_annot_name_id = layout->properties_repository ().get_id_of_name (dss.text_property_name ()); + text_annot_name_id = layout.properties_repository ().get_id_of_name (dss.text_property_name ()); } // gets the device terminal annotation property ID - // this is how the device extractor conveys terminal shape annotations. std::pair terminal_annot_name_id; - terminal_annot_name_id = layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_property_name ()); + terminal_annot_name_id = layout.properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_property_name ()); // the big part: actually extract the nets - m_net_clusters.build (*layout, cell, db::ShapeIterator::Polygons, conn); + m_net_clusters.build (layout, cell, db::ShapeIterator::Polygons, conn); // reverse lookup for Circuit vs. cell index std::map circuits; // some circuits may be there because of device extraction for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { - tl_assert (layout->is_valid_cell_index (c->cell_index ())); + tl_assert (layout.is_valid_cell_index (c->cell_index ())); circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); } std::map > pins_per_cluster; - for (db::Layout::bottom_up_const_iterator cid = layout->begin_bottom_up (); cid != layout->end_bottom_up (); ++cid) { + for (db::Layout::bottom_up_const_iterator cid = layout.begin_bottom_up (); cid != layout.end_bottom_up (); ++cid) { const connected_clusters_type &clusters = m_net_clusters.clusters_per_cell (*cid); if (clusters.empty ()) { @@ -305,7 +294,7 @@ public: if (k == circuits.end ()) { circuit = new db::Circuit (); nl->add_circuit (circuit); - circuit->set_name (layout->cell_name (*cid)); + circuit->set_name (layout.cell_name (*cid)); circuit->set_cell_index (*cid); circuits.insert (std::make_pair (*cid, circuit)); } else { @@ -348,7 +337,7 @@ public: tl_assert (k != circuits.end ()); // because we walk bottom-up subcircuit = new db::SubCircuit (k->second); - db::CplxTrans dbu_trans (layout->dbu ()); + db::CplxTrans dbu_trans (layout.dbu ()); subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); circuit->add_subcircuit (subcircuit); subcircuits.insert (std::make_pair (i->inst (), subcircuit)); @@ -371,7 +360,7 @@ public: const local_cluster_type &lc = clusters.cluster_by_id (*c); for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { - const db::PropertiesRepository::properties_set &ps = layout->properties_repository ().properties (*a); + const db::PropertiesRepository::properties_set &ps = layout.properties_repository ().properties (*a); for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { if (terminal_annot_name_id.first && j->first == terminal_annot_name_id.second) { @@ -624,15 +613,15 @@ TEST(1_DeviceNetExtraction) // NOTE: the device extractor will add more debug layers for the transistors: // 20/0 -> Diffusion // 21/0 -> Gate - MOSFETExtractor ex (nl, &ly); + MOSFETExtractor ex (&ly); - std::vector region_ptrs; - region_ptrs.push_back (&rpdiff); - region_ptrs.push_back (&rndiff); - region_ptrs.push_back (&rgate); - region_ptrs.push_back (&rpoly); + std::vector dl; + dl.push_back (rpdiff); + dl.push_back (rndiff); + dl.push_back (rgate); + dl.push_back (rpoly); - ex.extract (region_ptrs); + ex.extract (dss, dl, &nl); // perform the net extraction @@ -640,25 +629,25 @@ TEST(1_DeviceNetExtraction) db::Connectivity conn; // Intra-layer - conn.connect (layer_of (rpdiff)); - conn.connect (layer_of (rndiff)); - conn.connect (layer_of (rpoly)); - conn.connect (layer_of (rdiff_cont)); - conn.connect (layer_of (rpoly_cont)); - conn.connect (layer_of (rmetal1)); - conn.connect (layer_of (rvia1)); - conn.connect (layer_of (rmetal2)); + conn.connect (rpdiff); + conn.connect (rndiff); + conn.connect (rpoly); + conn.connect (rdiff_cont); + conn.connect (rpoly_cont); + conn.connect (rmetal1); + conn.connect (rvia1); + conn.connect (rmetal2); // Inter-layer - conn.connect (layer_of (rpdiff), layer_of (rdiff_cont)); - conn.connect (layer_of (rndiff), layer_of (rdiff_cont)); - conn.connect (layer_of (rpoly), layer_of (rpoly_cont)); - conn.connect (layer_of (rpoly_cont), layer_of (rmetal1)); - conn.connect (layer_of (rdiff_cont), layer_of (rmetal1)); - conn.connect (layer_of (rmetal1), layer_of (rvia1)); - conn.connect (layer_of (rvia1), layer_of (rmetal2)); - conn.connect (layer_of (rpoly), layer_of (rpoly_lbl)); // attaches labels - conn.connect (layer_of (rmetal1), layer_of (rmetal1_lbl)); // attaches labels - conn.connect (layer_of (rmetal2), layer_of (rmetal2_lbl)); // attaches labels + conn.connect (rpdiff, rdiff_cont); + conn.connect (rndiff, rdiff_cont); + conn.connect (rpoly, rpoly_cont); + conn.connect (rpoly_cont, rmetal1); + conn.connect (rdiff_cont, rmetal1); + conn.connect (rmetal1, rvia1); + conn.connect (rvia1, rmetal2); + conn.connect (rpoly, rpoly_lbl); // attaches labels + conn.connect (rmetal1, rmetal1_lbl); // attaches labels + conn.connect (rmetal2, rmetal2_lbl); // attaches labels // extract the nets From 8b2902c31bd1efd052d88ddfd3b7bf3f5351792a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 10:43:25 +0100 Subject: [PATCH 132/335] WIP: introduced expanded net name --- src/db/db/dbNetlist.cc | 14 ++++++++++++++ src/db/db/dbNetlist.h | 8 ++++++++ src/db/db/gsiDeclDbNetlist.cc | 4 ++++ src/db/unit_tests/dbNetlistDeviceExtractorTests.cc | 13 +------------ src/db/unit_tests/dbNetlistTests.cc | 5 +++++ testdata/ruby/dbNetlist.rb | 5 +++++ 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index ec2a987bd..bf5de8610 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -415,6 +415,20 @@ void Net::set_name (const std::string &name) m_name = name; } +std::string Net::expanded_name () const +{ + if (name ().empty ()) { + if (cluster_id () > std::numeric_limits::max () / 2) { + // avoid printing huge ID numbers for internal cluster IDs + return "$I" + tl::to_string ((std::numeric_limits::max () - cluster_id ()) + 1); + } else { + return "$" + tl::to_string (cluster_id ()); + } + } else { + return name (); + } +} + void Net::set_cluster_id (size_t ci) { m_cluster_id = ci; diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 09652cb08..cf44b1c84 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -352,6 +352,14 @@ public: return m_name; } + /** + * @brief Gets the expanded name + * + * The "expanded name" is a non-empty name for the net. It uses the + * cluster ID if no name is set. + */ + std::string expanded_name () const; + /** * @brief Sets the cluster ID of this net * diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index e210eccb2..32abccf50 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -236,6 +236,10 @@ Class decl_dbNet ("db", "Net", "@brief Gets the name of the net.\n" "See \\name= for details about the name." ) + + gsi::method ("expanded_name", &db::Net::expanded_name, + "@brief Gets the expanded name of the net.\n" + "The expanded name takes the name of the net. If the name is empty, the cluster ID will be used to build a name. " + ) + gsi::method ("cluster_id=", &db::Net::set_cluster_id, gsi::arg ("id"), "@brief Sets the cluster ID of the net.\n" "The cluster ID connects the net with a layout cluster. It is set when " diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index 97c648649..71bdba71f 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -405,20 +405,9 @@ private: hier_clusters_type m_net_clusters; }; -// @@@ TODO: move this somewhere else static std::string net_name (const db::Net *net) { - if (! net) { - return "(null)"; - } else if (net->name ().empty ()) { - if (net->cluster_id () > std::numeric_limits::max () / 2) { - return "$I" + tl::to_string ((std::numeric_limits::max () - net->cluster_id ()) + 1); - } else { - return "$" + tl::to_string (net->cluster_id ()); - } - } else { - return net->name (); - } + return net ? net->expanded_name () : "(null)"; } static std::string device_name (const db::Device &device) diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 138e4dd41..e1df6d1df 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -547,6 +547,11 @@ TEST(6_Net) n2 = n; EXPECT_EQ (n2.name (), "n"); EXPECT_EQ (int (n2.cluster_id ()), 17); + EXPECT_EQ (n2.expanded_name (), "n"); + n2.set_name (""); + EXPECT_EQ (n2.expanded_name (), "$17"); + n2.set_cluster_id (std::numeric_limits::max () - 2); + EXPECT_EQ (n2.expanded_name (), "$I3"); n.clear (); EXPECT_EQ (n.name (), ""); diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index bcd6314bc..eeead78df 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -342,9 +342,14 @@ class DBNetlist_TestClass < TestBase net.name = "NET" assert_equal(net.name, "NET") + assert_equal(net.expanded_name, "NET") net.cluster_id = 42 assert_equal(net.cluster_id, 42) + assert_equal(net.expanded_name, "NET") + + net.name = "" + assert_equal(net.expanded_name, "$42") end From d6473b4d846b90eb9a387653e71dcfae5ce3e579 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 10:57:46 +0100 Subject: [PATCH 133/335] WIP: moved netlist extractor into right place. --- src/db/db/db.pro | 6 +- src/db/db/dbNetlistExtractor.cc | 186 ++++++++++++++++++ src/db/db/dbNetlistExtractor.h | 95 +++++++++ .../dbNetlistDeviceExtractorTests.cc | 175 +--------------- 4 files changed, 287 insertions(+), 175 deletions(-) create mode 100644 src/db/db/dbNetlistExtractor.cc create mode 100644 src/db/db/dbNetlistExtractor.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 47346293b..65b36ade9 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -146,7 +146,8 @@ SOURCES = \ dbNetlist.cc \ gsiDeclDbNetlist.cc \ dbNetlistDeviceClasses.cc \ - dbNetlistDeviceExtractor.cc + dbNetlistDeviceExtractor.cc \ + dbNetlistExtractor.cc HEADERS = \ dbArray.h \ @@ -259,7 +260,8 @@ HEADERS = \ dbNetlistProperty.h \ dbNetlist.h \ dbNetlistDeviceClasses.h \ - dbNetlistDeviceExtractor.h + dbNetlistDeviceExtractor.h \ + dbNetlistExtractor.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc new file mode 100644 index 000000000..47c60afbf --- /dev/null +++ b/src/db/db/dbNetlistExtractor.cc @@ -0,0 +1,186 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlistExtractor.h" +#include "dbDeepShapeStore.h" +#include "dbNetlistDeviceExtractor.h" +#include "dbNetlistProperty.h" + +namespace db +{ + +NetlistExtractor::NetlistExtractor () +{ + // .. nothing yet .. +} + +void +NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl) +{ + const db::Layout &layout = dss.const_layout (); + const db::Cell &cell = dss.const_initial_cell (); + + // gets the text annotation property ID - + // this is how the texts are passed for annotating the net names + std::pair text_annot_name_id (false, 0); + if (! dss.text_property_name ().is_nil ()) { + text_annot_name_id = layout.properties_repository ().get_id_of_name (dss.text_property_name ()); + } + + // gets the device terminal annotation property ID - + // this is how the device extractor conveys terminal shape annotations. + std::pair terminal_annot_name_id; + terminal_annot_name_id = layout.properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_property_name ()); + + // the big part: actually extract the nets + + m_net_clusters.build (layout, cell, db::ShapeIterator::Polygons, conn); + + // reverse lookup for Circuit vs. cell index + std::map circuits; + + // some circuits may be there because of device extraction + for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { + tl_assert (layout.is_valid_cell_index (c->cell_index ())); + circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); + } + + std::map > pins_per_cluster; + + for (db::Layout::bottom_up_const_iterator cid = layout.begin_bottom_up (); cid != layout.end_bottom_up (); ++cid) { + + const connected_clusters_type &clusters = m_net_clusters.clusters_per_cell (*cid); + if (clusters.empty ()) { + continue; + } + + // a cell makes a new circuit (or uses an existing one) + + db::Circuit *circuit = 0; + + std::map::const_iterator k = circuits.find (*cid); + if (k == circuits.end ()) { + circuit = new db::Circuit (); + nl->add_circuit (circuit); + circuit->set_name (layout.cell_name (*cid)); + circuit->set_cell_index (*cid); + circuits.insert (std::make_pair (*cid, circuit)); + } else { + circuit = k->second; + } + + std::map &c2p = pins_per_cluster [*cid]; + + std::map subcircuits; + + for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { + + db::Net *net = new db::Net (); + net->set_cluster_id (*c); + circuit->add_net (net); + + if (! clusters.is_root (*c)) { + + // a non-root cluster makes a pin + db::Pin pin (net->name ()); + size_t pin_id = circuit->add_pin (pin).id (); + net->add_pin (db::NetPinRef (pin_id)); + c2p.insert (std::make_pair (*c, pin_id)); + circuit->connect_pin (pin_id, net); + + } + + const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (*c); + for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + + db::SubCircuit *subcircuit = 0; + db::cell_index_type ccid = i->inst ().inst_ptr.cell_index (); + + std::map::const_iterator j = subcircuits.find (i->inst ()); + if (j == subcircuits.end ()) { + + // make subcircuit if required + + std::map::const_iterator k = circuits.find (ccid); + tl_assert (k != circuits.end ()); // because we walk bottom-up + + subcircuit = new db::SubCircuit (k->second); + db::CplxTrans dbu_trans (layout.dbu ()); + subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); + circuit->add_subcircuit (subcircuit); + subcircuits.insert (std::make_pair (i->inst (), subcircuit)); + + } else { + subcircuit = j->second; + } + + // create the pin connection to the subcircuit + std::map >::const_iterator icc2p = pins_per_cluster.find (ccid); + tl_assert (icc2p != pins_per_cluster.end ()); + std::map::const_iterator ip = icc2p->second.find (i->id ()); + tl_assert (ip != icc2p->second.end ()); + subcircuit->connect_pin (ip->second, net); + + } + + // collect the properties - we know that the cluster attributes are property ID's because the + // cluster processor converts shape property IDs to attributes + const local_cluster_type &lc = clusters.cluster_by_id (*c); + for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { + + const db::PropertiesRepository::properties_set &ps = layout.properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + + if (terminal_annot_name_id.first && j->first == terminal_annot_name_id.second) { + + if (j->second.is_user ()) { + const db::NetlistProperty *np = &j->second.to_user (); + const db::DeviceTerminalProperty *tp = dynamic_cast (np); + if (tp) { + db::Device *device = circuit->device_by_id (tp->device_id ()); + tl_assert (device != 0); + device->connect_terminal (tp->terminal_id (), net); + } + } + + } else if (text_annot_name_id.first && j->first == text_annot_name_id.second) { + + std::string n = j->second.to_string (); + if (! n.empty ()) { + if (! net->name ().empty ()) { + n = net->name () + "," + n; + } + net->set_name (n); + } + + } + + } + + } + + } + + } +} + +} diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h new file mode 100644 index 000000000..ba1902b2b --- /dev/null +++ b/src/db/db/dbNetlistExtractor.h @@ -0,0 +1,95 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbNetlistExtractor +#define _HDR_dbNetlistExtractor + +#include "dbCommon.h" +#include "dbHierNetworkProcessor.h" + +namespace db +{ + +class DeepShapeStore; +class Netlist; + +/** + * @brief The Netlist Extractor + * + * This is the main object responsible for extracting nets from a layout. + * + * The layout needs to be present as a DeepShapeStore shadow layout. Use hierarchical regions + * (db::Region build with a DeepShapeStore) to populate the shape store. + * + * The extraction requires a connectivity definition through db::Connectivity. + * + * In addition, the device extraction needs to happen before net extraction. + * Device extraction will pre-fill the netlist with circuits and devices and + * annotate the layout with terminal shapes, so the net extraction can connect + * to the device terminals. + * + * If the deep shape store has been configured to supply text label annotated + * markers (DeepShapeStore::set_text_property_name and DeepShapeStore::set_text_enlargement + * to at least 1), texts from layers included in the connectivity will be extracted + * as net names. If multiple texts are present, the names will be concatenated using + * comma separators. + * + * Upon extraction, the given netlist is filled with circuits (unless present already), + * subcircuits, pins and of course nets. This object also supplies access to the net's + * geometries through the clusters() method. This method delivers a hierarchical + * cluster object. The nets refer to specific clusters through their "cluster_id" + * attribute. + */ +class DB_PUBLIC NetlistExtractor +{ +public: + typedef db::hier_clusters hier_clusters_type; + typedef db::connected_clusters connected_clusters_type; + typedef db::local_cluster local_cluster_type; + + /** + * @brief NetExtractor constructor + */ + NetlistExtractor (); + + /** + * @brief Extract the nets + * See the class description for more details. + */ + void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl); + + /** + * @brief Gets the shape clusters + * See the class description for more details. + */ + const hier_clusters_type &clusters () const + { + return m_net_clusters; + } + +private: + hier_clusters_type m_net_clusters; +}; + +} + +#endif diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index 71bdba71f..cd1c652a2 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -22,8 +22,8 @@ #include "dbNetlistDeviceExtractor.h" +#include "dbNetlistExtractor.h" #include "dbNetlistDeviceClasses.h" -#include "dbHierNetworkProcessor.h" #include "dbLayout.h" #include "dbDeepShapeStore.h" #include "dbRegion.h" @@ -234,177 +234,6 @@ static unsigned int layer_of (const db::Region ®ion) return db::DeepLayer (region).layer (); } -// @@@ TODO: move somewhere else -class NetExtractor -{ -public: - typedef db::hier_clusters hier_clusters_type; - typedef db::connected_clusters connected_clusters_type; - typedef db::local_cluster local_cluster_type; - - NetExtractor () - { - // .. nothing yet .. - } - - void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl) - { - const db::Layout &layout = dss.const_layout (); - const db::Cell &cell = dss.const_initial_cell (); - - // gets the text annotation property ID - - // this is how the texts are passed for annotating the net names - std::pair text_annot_name_id (false, 0); - if (! dss.text_property_name ().is_nil ()) { - text_annot_name_id = layout.properties_repository ().get_id_of_name (dss.text_property_name ()); - } - - // gets the device terminal annotation property ID - - // this is how the device extractor conveys terminal shape annotations. - std::pair terminal_annot_name_id; - terminal_annot_name_id = layout.properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_property_name ()); - - // the big part: actually extract the nets - - m_net_clusters.build (layout, cell, db::ShapeIterator::Polygons, conn); - - // reverse lookup for Circuit vs. cell index - std::map circuits; - - // some circuits may be there because of device extraction - for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { - tl_assert (layout.is_valid_cell_index (c->cell_index ())); - circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); - } - - std::map > pins_per_cluster; - - for (db::Layout::bottom_up_const_iterator cid = layout.begin_bottom_up (); cid != layout.end_bottom_up (); ++cid) { - - const connected_clusters_type &clusters = m_net_clusters.clusters_per_cell (*cid); - if (clusters.empty ()) { - continue; - } - - // a cell makes a new circuit (or uses an existing one) - - db::Circuit *circuit = 0; - - std::map::const_iterator k = circuits.find (*cid); - if (k == circuits.end ()) { - circuit = new db::Circuit (); - nl->add_circuit (circuit); - circuit->set_name (layout.cell_name (*cid)); - circuit->set_cell_index (*cid); - circuits.insert (std::make_pair (*cid, circuit)); - } else { - circuit = k->second; - } - - std::map &c2p = pins_per_cluster [*cid]; - - std::map subcircuits; - - for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { - - db::Net *net = new db::Net (); - net->set_cluster_id (*c); - circuit->add_net (net); - - if (! clusters.is_root (*c)) { - - // a non-root cluster makes a pin - db::Pin pin (net->name ()); - size_t pin_id = circuit->add_pin (pin).id (); - net->add_pin (db::NetPinRef (pin_id)); - c2p.insert (std::make_pair (*c, pin_id)); - circuit->connect_pin (pin_id, net); - - } - - const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (*c); - for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { - - db::SubCircuit *subcircuit = 0; - db::cell_index_type ccid = i->inst ().inst_ptr.cell_index (); - - std::map::const_iterator j = subcircuits.find (i->inst ()); - if (j == subcircuits.end ()) { - - // make subcircuit if required - - std::map::const_iterator k = circuits.find (ccid); - tl_assert (k != circuits.end ()); // because we walk bottom-up - - subcircuit = new db::SubCircuit (k->second); - db::CplxTrans dbu_trans (layout.dbu ()); - subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); - circuit->add_subcircuit (subcircuit); - subcircuits.insert (std::make_pair (i->inst (), subcircuit)); - - } else { - subcircuit = j->second; - } - - // create the pin connection to the subcircuit - std::map >::const_iterator icc2p = pins_per_cluster.find (ccid); - tl_assert (icc2p != pins_per_cluster.end ()); - std::map::const_iterator ip = icc2p->second.find (i->id ()); - tl_assert (ip != icc2p->second.end ()); - subcircuit->connect_pin (ip->second, net); - - } - - // collect the properties - we know that the cluster attributes are property ID's because the - // cluster processor converts shape property IDs to attributes - const local_cluster_type &lc = clusters.cluster_by_id (*c); - for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { - - const db::PropertiesRepository::properties_set &ps = layout.properties_repository ().properties (*a); - for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { - - if (terminal_annot_name_id.first && j->first == terminal_annot_name_id.second) { - - if (j->second.is_user ()) { - const db::NetlistProperty *np = &j->second.to_user (); - const db::DeviceTerminalProperty *tp = dynamic_cast (np); - if (tp) { - db::Device *device = circuit->device_by_id (tp->device_id ()); - tl_assert (device != 0); - device->connect_terminal (tp->terminal_id (), net); - } - } - - } else if (text_annot_name_id.first && j->first == text_annot_name_id.second) { - - std::string n = j->second.to_string (); - if (! n.empty ()) { - if (! net->name ().empty ()) { - n = net->name () + "," + n; - } - net->set_name (n); - } - - } - - } - - } - - } - - } - } - - const hier_clusters_type clusters () const - { - return m_net_clusters; - } - -private: - hier_clusters_type m_net_clusters; -}; - static std::string net_name (const db::Net *net) { return net ? net->expanded_name () : "(null)"; @@ -614,7 +443,7 @@ TEST(1_DeviceNetExtraction) // perform the net extraction - NetExtractor net_ex; + db::NetlistExtractor net_ex; db::Connectivity conn; // Intra-layer From 1c41c43b952020f282c4baaafc56cb8cde8b9478 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 10:58:48 +0100 Subject: [PATCH 134/335] WIP: rename netlist extractor test. --- ...etlistDeviceExtractorTests.cc => dbNetlistExtractorTests.cc} | 2 +- src/db/unit_tests/unit_tests.pro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/db/unit_tests/{dbNetlistDeviceExtractorTests.cc => dbNetlistExtractorTests.cc} (99%) diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc similarity index 99% rename from src/db/unit_tests/dbNetlistDeviceExtractorTests.cc rename to src/db/unit_tests/dbNetlistExtractorTests.cc index cd1c652a2..581c03572 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -355,7 +355,7 @@ static std::string netlist2string (const db::Netlist &nl) return res; } -TEST(1_DeviceNetExtraction) +TEST(1_DeviceAndNetExtraction) { db::Layout ly; db::LayerMap lmap; diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index ac3cd3382..16e32217c 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -61,7 +61,7 @@ SOURCES = \ dbHierNetworkProcessorTests.cc \ dbNetlistPropertyTests.cc \ dbNetlistTests.cc \ - dbNetlistDeviceExtractorTests.cc + dbNetlistExtractorTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC From 473c90f99db58868b88a747336fc4c82821f3367 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 19:06:16 +0100 Subject: [PATCH 135/335] WIP: some refactoring: splitting of big methods, documentation --- src/db/db/dbNetlistExtractor.cc | 146 ++++++++++++++++++-------------- src/db/db/dbNetlistExtractor.h | 45 ++++++++++ 2 files changed, 129 insertions(+), 62 deletions(-) diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 47c60afbf..05d492366 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -99,83 +99,26 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect circuit->add_net (net); if (! clusters.is_root (*c)) { - // a non-root cluster makes a pin - db::Pin pin (net->name ()); - size_t pin_id = circuit->add_pin (pin).id (); - net->add_pin (db::NetPinRef (pin_id)); + size_t pin_id = make_pin (circuit, net); c2p.insert (std::make_pair (*c, pin_id)); - circuit->connect_pin (pin_id, net); - } - const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (*c); - for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { - - db::SubCircuit *subcircuit = 0; - db::cell_index_type ccid = i->inst ().inst_ptr.cell_index (); - - std::map::const_iterator j = subcircuits.find (i->inst ()); - if (j == subcircuits.end ()) { - - // make subcircuit if required - - std::map::const_iterator k = circuits.find (ccid); - tl_assert (k != circuits.end ()); // because we walk bottom-up - - subcircuit = new db::SubCircuit (k->second); - db::CplxTrans dbu_trans (layout.dbu ()); - subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); - circuit->add_subcircuit (subcircuit); - subcircuits.insert (std::make_pair (i->inst (), subcircuit)); - - } else { - subcircuit = j->second; - } - - // create the pin connection to the subcircuit - std::map >::const_iterator icc2p = pins_per_cluster.find (ccid); - tl_assert (icc2p != pins_per_cluster.end ()); - std::map::const_iterator ip = icc2p->second.find (i->id ()); - tl_assert (ip != icc2p->second.end ()); - subcircuit->connect_pin (ip->second, net); - - } + // make subcircuit connections (also make the subcircuits if required) from the connections of the clusters + make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster, layout.dbu ()); // collect the properties - we know that the cluster attributes are property ID's because the // cluster processor converts shape property IDs to attributes const local_cluster_type &lc = clusters.cluster_by_id (*c); for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { - const db::PropertiesRepository::properties_set &ps = layout.properties_repository ().properties (*a); for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { - if (terminal_annot_name_id.first && j->first == terminal_annot_name_id.second) { - - if (j->second.is_user ()) { - const db::NetlistProperty *np = &j->second.to_user (); - const db::DeviceTerminalProperty *tp = dynamic_cast (np); - if (tp) { - db::Device *device = circuit->device_by_id (tp->device_id ()); - tl_assert (device != 0); - device->connect_terminal (tp->terminal_id (), net); - } - } - + make_device_terminal_from_property (j->second, circuit, net); } else if (text_annot_name_id.first && j->first == text_annot_name_id.second) { - - std::string n = j->second.to_string (); - if (! n.empty ()) { - if (! net->name ().empty ()) { - n = net->name () + "," + n; - } - net->set_name (n); - } - + make_net_name_from_property (j->second, net); } - } - } } @@ -183,4 +126,83 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } } +void NetlistExtractor::make_and_connect_subcircuits (db::Circuit *circuit, + const connected_clusters_type &clusters, + size_t cid, + db::Net *net, + std::map &subcircuits, + const std::map &circuits, + const std::map > &pins_per_cluster, + double dbu) +{ + const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (cid); + for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + + db::SubCircuit *subcircuit = 0; + db::cell_index_type ccid = i->inst ().inst_ptr.cell_index (); + + std::map::const_iterator j = subcircuits.find (i->inst ()); + if (j == subcircuits.end ()) { + + // make subcircuit if required + + std::map::const_iterator k = circuits.find (ccid); + tl_assert (k != circuits.end ()); // because we walk bottom-up + + subcircuit = new db::SubCircuit (k->second); + db::CplxTrans dbu_trans (dbu); + subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); + circuit->add_subcircuit (subcircuit); + subcircuits.insert (std::make_pair (i->inst (), subcircuit)); + + } else { + subcircuit = j->second; + } + + // create the pin connection to the subcircuit + std::map >::const_iterator icc2p = pins_per_cluster.find (ccid); + tl_assert (icc2p != pins_per_cluster.end ()); + std::map::const_iterator ip = icc2p->second.find (i->id ()); + tl_assert (ip != icc2p->second.end ()); + subcircuit->connect_pin (ip->second, net); + + } +} + +size_t NetlistExtractor::make_pin (db::Circuit *circuit, db::Net *net) +{ + db::Pin pin (net->name ()); + + size_t pin_id = circuit->add_pin (pin).id (); + net->add_pin (db::NetPinRef (pin_id)); + + circuit->connect_pin (pin_id, net); + + return pin_id; +} + +void NetlistExtractor::make_device_terminal_from_property (const tl::Variant &v, db::Circuit *circuit, db::Net *net) +{ + if (v.is_user ()) { + const db::NetlistProperty *np = &v.to_user (); + const db::DeviceTerminalProperty *tp = dynamic_cast (np); + if (tp) { + db::Device *device = circuit->device_by_id (tp->device_id ()); + tl_assert (device != 0); + device->connect_terminal (tp->terminal_id (), net); + } + } +} + +void NetlistExtractor::make_net_name_from_property (const tl::Variant &v, db::Net *net) +{ + std::string n = v.to_string (); + if (! n.empty ()) { + if (! net->name ().empty ()) { + n = net->name () + "," + n; + } + net->set_name (n); + } +} + } diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index ba1902b2b..b95953ecf 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -26,11 +26,16 @@ #include "dbCommon.h" #include "dbHierNetworkProcessor.h" +#include + namespace db { class DeepShapeStore; class Netlist; +class Circuit; +class SubCircuit; +class Net; /** * @brief The Netlist Extractor @@ -88,6 +93,46 @@ public: private: hier_clusters_type m_net_clusters; + + void make_device_terminal_from_property (const tl::Variant &v, db::Circuit *circuit, db::Net *net); + void make_net_name_from_property (const tl::Variant &v, db::Net *net); + + /** + * @brief Make a pin connection from clusters + * + * Non-root clusters make a pin. This function creates the pin inside the given circuit. It + * returns the new pin's ID. + */ + size_t make_pin (db::Circuit *circuit, db::Net *net); + + /** + * @brief Turns the connections of a cluster into subcircuit instances + * + * Walks through the connections of a cluster and turns the connections into subcircuit pin + * connections. This will also create new subcircuit instances. + * + * Needs: + * - circuit: the current circuit that is worked on + * - clusters: the connected clusters from the net extraction step (nets plus + * connections to child cell clusters inside the current cell) + * - cid: the current cluster ID in "clusters" + * - net: the net being built + * - circuits: a lookup table of circuits vs. cell index (reverse lookup) + * - pins_per_cluster: a per-cell, reverse lookup table for the pin id per child cell clusters + * (used to find the pin ID of a subcircuit from the child cell cluster ID) + * - dbu: the database unit (used to compute the micron-unit transformation of the child + * cell) + * Updates: + * - subcircuits: An cell instance to SubCircuit lookup table + */ + void make_and_connect_subcircuits (db::Circuit *circuit, + const connected_clusters_type &clusters, + size_t cid, + db::Net *net, + std::map &subcircuits, + const std::map &circuits, + const std::map > &pins_per_cluster, + double dbu); }; } From 411c18cdb4f5a7f7e315fc5420f8d51d9ed28df5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Dec 2018 21:06:35 +0100 Subject: [PATCH 136/335] WIP: also test parameter extraction in device extraction test. --- src/db/unit_tests/dbNetlistExtractorTests.cc | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 581c03572..220e787d9 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -334,7 +334,15 @@ static std::string netlist2string (const db::Netlist &nl) } ts += t->name () + "=" + net_name (d->net_for_terminal (t->id ())); } - res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d) + " (" + ts + ")\n"; + std::string ps; + const std::vector &pd = d->device_class ()->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (p != pd.begin ()) { + ps += ","; + } + ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ())); + } + res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d) + " (" + ts + ") [" + ps + "]\n"; } for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { @@ -507,10 +515,10 @@ TEST(1_DeviceAndNetExtraction) " XINV2 $9 ($1=$I6,$2=$I45,$3=$I7,$4=VSS,$5=VDD)\n" " XINV2 $10 ($1=$I7,$2=$I46,$3=$I8,$4=VSS,$5=VDD)\n" "Circuit INV2 ($1=IN,$2=$2,$3=OUT,$4=$4,$5=$5):\n" - " DPMOS 1 (S=$2,G=IN,D=$5)\n" - " DPMOS 2 (S=$5,G=$2,D=OUT)\n" - " DNMOS 3 (S=$2,G=IN,D=$4)\n" - " DNMOS 4 (S=$4,G=$2,D=OUT)\n" + " DPMOS 1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS 2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS 3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS 4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" From c665d6acebdb319e9d997698d94704bdbcaf1794 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 28 Dec 2018 01:06:17 +0100 Subject: [PATCH 137/335] WIP: introduced error messaging for device extractor. --- src/db/db/dbNetlistDeviceExtractor.cc | 62 +++++++ src/db/db/dbNetlistDeviceExtractor.h | 174 ++++++++++++++++++ .../dbNetlistDeviceExtractorTests.cc | 88 +++++++++ src/db/unit_tests/dbNetlistExtractorTests.cc | 115 +++++++++--- src/db/unit_tests/unit_tests.pro | 3 +- 5 files changed, 416 insertions(+), 26 deletions(-) create mode 100644 src/db/unit_tests/dbNetlistDeviceExtractorTests.cc diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 09787dd46..ad7aa001e 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -29,6 +29,23 @@ namespace db { +// ---------------------------------------------------------------------------------------- +// NetlistDeviceExtractorError implementation + +NetlistDeviceExtractorError::NetlistDeviceExtractorError () +{ + // .. nothing yet .. +} + +NetlistDeviceExtractorError::NetlistDeviceExtractorError (const std::string &cell_name, const std::string &msg) + : m_cell_name (cell_name), m_message (msg) +{ + // .. nothing yet .. +} + +// ---------------------------------------------------------------------------------------- +// NetlistDeviceExtractor implementation + NetlistDeviceExtractor::NetlistDeviceExtractor () : mp_layout (0), m_cell_index (0), mp_circuit (0) { @@ -219,4 +236,49 @@ void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id define_terminal (device, terminal_id, layer_index, db::Polygon (db::Box (point - dv, point + dv))); } +std::string NetlistDeviceExtractor::cell_name () const +{ + if (layout ()) { + return layout ()->cell_name (cell_index ()); + } else { + return std::string (); + } +} + +void NetlistDeviceExtractor::error (const std::string &msg) +{ + m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); +} + +void NetlistDeviceExtractor::error (const std::string &msg, const db::Polygon &poly) +{ + error (msg); + m_errors.back ().set_geometry (db::Region (poly)); +} + +void NetlistDeviceExtractor::error (const std::string &msg, const db::Region ®ion) +{ + error (msg); + m_errors.back ().set_geometry (region); +} + +void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg) +{ + error (msg); + m_errors.back ().set_category_name (category_name); + m_errors.back ().set_category_description (category_description); +} + +void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Polygon &poly) +{ + error (category_name, category_description, msg); + m_errors.back ().set_geometry (db::Region (poly)); +} + +void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Region ®ion) +{ + error (category_name, category_description, msg); + m_errors.back ().set_geometry (region); +} + } diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index 4f99cc1ba..73430901e 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -28,12 +28,123 @@ #include "dbLayout.h" #include "dbHierNetworkProcessor.h" #include "dbDeepShapeStore.h" +#include "dbRegion.h" #include "gsiObject.h" namespace db { +/** + * @brief An error object for the netlist device extractor + * + * The device extractor will keep errors using objects of this kind. + */ +class DB_PUBLIC NetlistDeviceExtractorError +{ +public: + /** + * @brief Creates an error + */ + NetlistDeviceExtractorError (); + + /** + * @brief Creates an error with a cell name and a message (the minimum information) + */ + NetlistDeviceExtractorError (const std::string &cell_name, const std::string &msg); + + /** + * @brief The category name of the error + * Specifying the category name is optional. If a category is given, it will be used for + * the report. + */ + const std::string &category_name () const + { + return m_category_name; + } + + /** + * @brief Sets the category name + */ + void set_category_name (const std::string &s) + { + m_category_name = s; + } + + /** + * @brief The category description of the error + * Specifying the category description is optional. If a category is given, this attribute will + * be used for the category description. + */ + const std::string &category_description () const + { + return m_category_description; + } + + /** + * @brief Sets the category description + */ + void set_category_description (const std::string &s) + { + m_category_description = s; + } + + /** + * @brief Gets the geometry for this error + * Not all errors may specify a geometry. + */ + const db::Region &geometry () const + { + return m_geometry; + } + + /** + * @brief Sets the geometry + */ + void set_geometry (const db::Region &g) + { + m_geometry = g; + } + + /** + * @brief Gets the message for this error + */ + const std::string &message () const + { + return m_message; + } + + /** + * @brief Sets the message + */ + void set_message (const std::string &n) + { + m_message = n; + } + + /** + * @brief Gets the cell name the error occured in + */ + const std::string &cell_name () const + { + return m_cell_name; + } + + /** + * @brief Sets the cell name + */ + void set_cell_name (const std::string &n) + { + m_cell_name = n; + } + +private: + std::string m_cell_name; + std::string m_message; + db::Region m_geometry; + std::string m_category_name, m_category_description; +}; + /** * @brief Implements the device extraction for a specific setup * @@ -44,6 +155,9 @@ class DB_PUBLIC NetlistDeviceExtractor : public gsi::ObjectBase { public: + typedef std::list error_list; + typedef error_list::const_iterator error_iterator; + /** * @brief Default constructor */ @@ -92,6 +206,30 @@ public: */ void extract (DeepShapeStore &dss, const std::vector &layers, Netlist *netlist); + /** + * @brief Gets the error iterator, begin + */ + error_iterator begin_errors () + { + return m_errors.begin (); + } + + /** + * @brief Gets the error iterator, end + */ + error_iterator end_errors () + { + return m_errors.end (); + } + + /** + * @brief Returns true, if there are errors + */ + bool has_errors () const + { + return ! m_errors.empty (); + } + protected: /** * @brief Creates the device classes @@ -186,6 +324,41 @@ protected: return m_cell_index; } + /** + * @brief Issues an error with the given message + */ + void error (const std::string &msg); + + /** + * @brief Issues an error with the given message and error shape + */ + void error (const std::string &msg, const db::Polygon &poly); + + /** + * @brief Issues an error with the given message and error geometry + */ + void error (const std::string &msg, const db::Region ®ion); + + /** + * @brief Issues an error with the given category name, description and message + */ + void error (const std::string &category_name, const std::string &category_description, const std::string &msg); + + /** + * @brief Issues an error with the given category name, description and message and error shape + */ + void error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Polygon &poly); + + /** + * @brief Issues an error with the given category name, description and message and error geometry + */ + void error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Region ®ion); + + /** + * @brief Gets the name of the current cell + */ + std::string cell_name () const; + private: tl::weak_ptr m_netlist; db::Layout *mp_layout; @@ -195,6 +368,7 @@ private: std::vector m_device_classes; std::vector m_layers; unsigned int m_device_name_index; + error_list m_errors; /** * @brief Initializes the extractor diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc new file mode 100644 index 000000000..a9eaa5ec8 --- /dev/null +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -0,0 +1,88 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlistDeviceExtractor.h" + +#include "tlUnitTest.h" + +TEST(1_NetlistDeviceExtractorErrorBasic) +{ + db::NetlistDeviceExtractorError error; + + EXPECT_EQ (error.message (), ""); + error.set_message ("x"); + EXPECT_EQ (error.message (), "x"); + error.set_category_name ("cat"); + EXPECT_EQ (error.category_name (), "cat"); + error.set_category_description ("cdesc"); + EXPECT_EQ (error.category_description (), "cdesc"); + error.set_cell_name ("cell"); + EXPECT_EQ (error.cell_name (), "cell"); + error.set_geometry (db::Region (db::Box (0, 1, 2, 3))); + EXPECT_EQ (error.geometry ().to_string (), "(0,1;0,3;2,3;2,1)"); + + error = db::NetlistDeviceExtractorError ("cell2", "msg2"); + EXPECT_EQ (error.cell_name (), "cell2"); + EXPECT_EQ (error.message (), "msg2"); + EXPECT_EQ (error.category_name (), ""); + EXPECT_EQ (error.category_description (), ""); + EXPECT_EQ (error.geometry ().to_string (), ""); +} + +namespace { + class DummyDeviceExtractor + : public db::NetlistDeviceExtractor + { + public: + DummyDeviceExtractor () + { + error ("msg1"); + error ("msg2", db::Box (0, 1, 2, 3)); + error ("msg3", db::Region (db::Box (10, 11, 12, 13))); + error ("cat1", "desc1", "msg1"); + error ("cat1", "desc1", "msg2", db::Box (0, 1, 2, 3)); + error ("cat1", "desc1", "msg3", db::Region (db::Box (10, 11, 12, 13))); + } + }; +} + +static std::string error2string (const db::NetlistDeviceExtractorError &e) +{ + return e.cell_name() + ":" + e.category_name () + ":" + e.category_description () + ":" + + e.geometry ().to_string () + ":" + e.message (); +} + +TEST(2_NetlistDeviceExtractorErrors) +{ + DummyDeviceExtractor dummy_ex; + + EXPECT_EQ (dummy_ex.has_errors (), true); + + std::vector errors (dummy_ex.begin_errors (), dummy_ex.end_errors ()); + EXPECT_EQ (int (errors.size ()), 6); + EXPECT_EQ (error2string (errors [0]), "::::msg1"); + EXPECT_EQ (error2string (errors [1]), ":::(0,1;0,3;2,3;2,1):msg2"); + EXPECT_EQ (error2string (errors [2]), ":::(10,11;10,13;12,13;12,11):msg3"); + EXPECT_EQ (error2string (errors [3]), ":cat1:desc1::msg1"); + EXPECT_EQ (error2string (errors [4]), ":cat1:desc1:(0,1;0,3;2,3;2,1):msg2"); + EXPECT_EQ (error2string (errors [5]), ":cat1:desc1:(10,11;10,13;12,13;12,11):msg3"); +} diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 220e787d9..020d5b3fe 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -161,30 +161,6 @@ public: } } - void error (const std::string &msg) - { - // @@@ TODO: move this to device extractor - tl::error << tr ("Error in cell '") << cell_name () << "': " << msg; - } - - void error (const std::string &msg, const db::Polygon &poly) - { - // @@@ TODO: move this to device extractor - tl::error << tr ("Error in cell '") << cell_name () << "': " << msg << " (" << poly.to_string () << ")"; - } - - void error (const std::string &msg, const db::Region ®ion) - { - // @@@ TODO: move this to device extractor - tl::error << tr ("Error in cell '") << cell_name () << "': " << msg << " (" << region.to_string () << ")"; - } - - std::string cell_name () const - { - // @@@ TODO: move this to device extractor - return layout ()->cell_name (cell_index ()); - } - private: db::Layout *mp_debug_out; unsigned int m_ldiff, m_lgate; @@ -363,7 +339,7 @@ static std::string netlist2string (const db::Netlist &nl) return res; } -TEST(1_DeviceAndNetExtraction) +TEST(2_DeviceAndNetExtraction) { db::Layout ly; db::LayerMap lmap; @@ -535,3 +511,92 @@ TEST(1_DeviceAndNetExtraction) db::compare_layouts (_this, ly, au); } + +#if 0 + +// -------------------------------------------------------------------------------------- +// An attempt to simplify things. + +/* +- layers: use db::Region, or wrapper? +-> use regions, but test whether they are deep regions (?) + +TODO: +- netlist query functions such as net_by_name, device_by_name, circuit_by_name +- terminal geometry (Polygon) for device, combined device geometry (all terminals) +- error interface for device extraction + // gets the device extraction errors + // device_extraction_error_iterator begin_device_extraction_errors () const; + // device_extraction_error_iterator end_device_extraction_errors () const; + // bool has_device_extraction_errors () const; + +- device extractor needs to declare the layers to allow passing them by name +- netlist manipulation methods (i.e. flatten certain cells, purging etc.) +*/ + +#include "tlGlobPattern.h" +#include "dbHierNetworkProcessor.h" + +namespace db +{ + +class DB_PUBLIC LayoutToNetlist +{ +public: + // the iterator provides the hierarchical selection (enabling/disabling cells etc.) + LayoutToNetlist (const db::RecursiveShapeIterator &iter); + + // --- preparation + + // returns a new'd region + db::Region *make_layer (unsigned int layer_index); + db::Region *make_text_layer (unsigned int layer_index); + db::Region *make_polygon_layer (unsigned int layer_index); + + // gets the internal layout and cell + const db::Layout &internal_layout () const; + const db::Cell &internal_top_cell () const; + + // --- device extraction + + // after this, the device extractor will have errors if some occured. + void extract_devices (db::NetlistDeviceExtractor *extractor, const std::map &layers); + + // --- net extraction + + // define connectivity for the netlist extraction + void connect (const db::Region &l); + void connect (const db::Region &a, const db::Region &b); + + // runs the netlist extraction + void extract_netlist (); + + // --- retrieval + + // gets the internal layer index of the given region + unsigned int layer_of (const db::Region ®ion) const; + + // creates a cell mapping for copying the internal hierarchy to the given layout + // CAUTION: may create new cells in "layout". + db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell); + + // creates a cell mapping for copying the internal hierarchy to the given layout + // This version will not create new cells in the target layout. + db::CellMapping const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell); + + // gets the netlist extracted (0 if no extraction happened yet) + db::Netlist *netlist () const; + + // gets the hierarchical clusters of the nets (CAUTION: the layer indexes therein are + // internal layer indexes), same for cell indexes. + // -> NOT GSI + const db::hier_clusters &net_clusters () const; + + // copies the shapes of the given net from a given layer + // (recursive true: include nets from subcircuits) + db::Region shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive); +}; + +} + +#endif diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 16e32217c..a6f455bac 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -61,7 +61,8 @@ SOURCES = \ dbHierNetworkProcessorTests.cc \ dbNetlistPropertyTests.cc \ dbNetlistTests.cc \ - dbNetlistExtractorTests.cc + dbNetlistExtractorTests.cc \ + dbNetlistDeviceExtractorTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC From a5b9cbfe5b904cc7d928ba2d0f6f2eb0c086696c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 28 Dec 2018 01:53:27 +0100 Subject: [PATCH 138/335] WIP: device extractor now declares it's layers, passing layers by name supported now. --- src/db/db/dbNetlistDeviceExtractor.cc | 47 ++++++++--- src/db/db/dbNetlistDeviceExtractor.h | 82 +++++++++++++++++++- src/db/unit_tests/dbNetlistExtractorTests.cc | 17 ++-- 3 files changed, 126 insertions(+), 20 deletions(-) diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index ad7aa001e..5664a0884 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -66,12 +66,13 @@ const tl::Variant &NetlistDeviceExtractor::terminal_property_name () void NetlistDeviceExtractor::initialize (db::Netlist *nl) { + m_layer_definitions.clear (); m_device_classes.clear (); m_device_name_index = 0; m_propname_id = 0; m_netlist.reset (nl); - create_device_classes (); + setup (); } static void insert_into_region (const db::PolygonRef &s, const db::ICplxTrans &tr, db::Region ®ion) @@ -79,25 +80,46 @@ static void insert_into_region (const db::PolygonRef &s, const db::ICplxTrans &t region.insert (s.obj ().transformed (tr * db::ICplxTrans (s.trans ()))); } -void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const std::vector &deep_layers, db::Netlist *nl) +void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDeviceExtractor::input_layers &layer_map, db::Netlist *nl) { - db::Layout &layout = dss.layout (); - db::Cell &cell = dss.initial_cell (); + initialize (nl); std::vector layers; - layers.reserve (deep_layers.size ()); + layers.reserve (m_layer_definitions.size ()); + + for (layer_definitions::const_iterator ld = begin_layer_definitions (); ld != end_layer_definitions (); ++ld) { + + input_layers::const_iterator l = layer_map.find (ld->name); + if (l == layer_map.end ()) { + throw tl::Exception (tl::to_string (tr ("Missing input layer for device extraction: ")) + ld->name); + } + + tl_assert (l->second != 0); + db::DeepRegion *dr = dynamic_cast (l->second->delegate ()); + if (dr == 0) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: must be of deep region kind")), ld->name)); + } + + if (&dr->deep_layer ().layout () != &dss.layout () || &dr->deep_layer ().initial_cell () != &dss.initial_cell ()) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: not originating from the same source")), ld->name)); + } + + layers.push_back (dr->deep_layer ().layer ()); - for (std::vector::const_iterator dl = deep_layers.begin (); dl != deep_layers.end (); ++dl) { - tl_assert (&dl->layout () == &layout); - layers.push_back (dl->layer ()); } - extract (layout, cell, layers, nl); + extract_without_initialize (dss.layout (), dss.initial_cell (), layers, nl); } void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl) { initialize (nl); + extract_without_initialize (layout, cell, layers, nl); +} + +void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl) +{ + tl_assert (layers.size () == m_layer_definitions.size ()); typedef db::PolygonRef shape_type; db::ShapeIterator::flags_type shape_iter_flags = db::ShapeIterator::Polygons; @@ -176,7 +198,7 @@ void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const } } -void NetlistDeviceExtractor::create_device_classes () +void NetlistDeviceExtractor::setup () { // .. the default implementation does nothing .. } @@ -199,6 +221,11 @@ void NetlistDeviceExtractor::register_device_class (DeviceClass *device_class) m_device_classes.push_back (device_class); } +void NetlistDeviceExtractor::define_layer (const std::string &name, const std::string &description) +{ + m_layer_definitions.push_back (db::NetlistDeviceExtractorLayerDefinition (name, description, m_layer_definitions.size ())); +} + Device *NetlistDeviceExtractor::create_device (unsigned int device_class_index) { tl_assert (mp_circuit != 0); diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index 73430901e..5f4d458cf 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -145,6 +145,40 @@ private: std::string m_category_name, m_category_description; }; +/** + * @brief Specifies a single layer from the device extractor + */ +class DB_PUBLIC NetlistDeviceExtractorLayerDefinition +{ +public: + NetlistDeviceExtractorLayerDefinition () + : index (0) + { + // .. nothing yet .. + } + + NetlistDeviceExtractorLayerDefinition (const std::string &_name, const std::string &_description, size_t _index) + : name (_name), description (_description), index (_index) + { + // .. nothing yet .. + } + + /** + * @brief The formal name + */ + std::string name; + + /** + * @brief The human-readable description + */ + std::string description; + + /** + * @brief The index of the layer + */ + size_t index; +}; + /** * @brief Implements the device extraction for a specific setup * @@ -157,6 +191,9 @@ class DB_PUBLIC NetlistDeviceExtractor public: typedef std::list error_list; typedef error_list::const_iterator error_iterator; + typedef std::vector layer_definitions; + typedef layer_definitions::const_iterator layer_definitions_iterator; + typedef std::map input_layers; /** * @brief Default constructor @@ -202,9 +239,10 @@ public: * @brief Extracts the devices from a list of regions * * This method behaves identical to the other "extract" method, but accepts - * DeepShape layers for input. By definition, these already have the "PolygonRef" type. + * named regions for input. These regions need to be of deep region type and + * originate from the same layout than the DeepShapeStore. */ - void extract (DeepShapeStore &dss, const std::vector &layers, Netlist *netlist); + void extract (DeepShapeStore &dss, const input_layers &layers, Netlist *netlist); /** * @brief Gets the error iterator, begin @@ -230,14 +268,36 @@ public: return ! m_errors.empty (); } + /** + * @brief Gets the layer definition iterator, begin + */ + layer_definitions_iterator begin_layer_definitions () const + { + return m_layer_definitions.begin (); + } + + /** + * @brief Gets the layer definition iterator, end + */ + layer_definitions_iterator end_layer_definitions () const + { + return m_layer_definitions.end (); + } + protected: /** - * @brief Creates the device classes + * @brief Sets up the extractor + * + * This method is supposed to set up the device extractor. This involves two basic steps: + * defining the device classes and setting up the device layers. + * * At least one device class needs to be defined. Use "register_device_class" to register * the device classes you need. The first device class registered has device class index 0, * the further ones 1, 2, etc. + * + * The device layers need to be defined by calling "define_layer" once or several times. */ - virtual void create_device_classes (); + virtual void setup (); /** * @brief Gets the connectivity object used to extract the device geometry @@ -264,9 +324,20 @@ protected: * @brief Registers a device class * The device class object will become owned by the netlist and must not be deleted by * the caller. + * This method shall be used inside the implementation of "setup" to register + * the device classes. */ void register_device_class (DeviceClass *device_class); + /** + * @brief Defines a layer + * Each call will define one more layer for the device extraction. + * This method shall be used inside the implementation of "setip" to define + * the device layers. The actual geometries are later available to "extract_devices" + * in the order the layers are defined. + */ + void define_layer (const std::string &name, const std::string &description = std::string ()); + /** * @brief Creates a device * The device object returned can be configured by the caller, e.g. set parameters. @@ -366,6 +437,7 @@ private: db::cell_index_type m_cell_index; db::Circuit *mp_circuit; std::vector m_device_classes; + layer_definitions m_layer_definitions; std::vector m_layers; unsigned int m_device_name_index; error_list m_errors; @@ -375,6 +447,8 @@ private: * This method will produce the device classes required for the device extraction. */ void initialize (db::Netlist *nl); + + void extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl); }; } diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 020d5b3fe..bbf7c72ab 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -57,8 +57,13 @@ public: } } - virtual void create_device_classes () + virtual void setup () { + define_layer ("PD", "P diffusion"); + define_layer ("ND", "N diffusion"); + define_layer ("G", "Gate"); + define_layer ("P", "Poly"); + db::DeviceClassMOS3Transistor *pmos_class = new db::DeviceClassMOS3Transistor (); pmos_class->set_name ("PMOS"); register_device_class (pmos_class); @@ -417,11 +422,11 @@ TEST(2_DeviceAndNetExtraction) // 21/0 -> Gate MOSFETExtractor ex (&ly); - std::vector dl; - dl.push_back (rpdiff); - dl.push_back (rndiff); - dl.push_back (rgate); - dl.push_back (rpoly); + db::NetlistDeviceExtractor::input_layers dl; + dl["PD"] = &rpdiff; + dl["ND"] = &rndiff; + dl["G"] = &rgate; + dl["P"] = &rpoly; ex.extract (dss, dl, &nl); From 2f484798383a3786fc7af4bf7d01f952e82525a2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 28 Dec 2018 22:51:11 +0100 Subject: [PATCH 139/335] WIP: a bit of simplification, renaming of methods, parents for subcircuits, devices. References for circuits pointing to subcircuits. --- src/db/db/dbNetlist.cc | 54 ++- src/db/db/dbNetlist.h | 106 ++++- src/db/db/dbNetlistDeviceExtractor.cc | 31 +- src/db/db/dbNetlistDeviceExtractor.h | 32 +- src/db/db/gsiDeclDbNetlist.cc | 12 +- .../dbNetlistDeviceExtractorTests.cc | 1 + src/db/unit_tests/dbNetlistExtractorTests.cc | 424 +++++++++--------- src/db/unit_tests/dbNetlistTests.cc | 64 ++- testdata/algo/device_extract_au1.gds | Bin 8774 -> 9014 bytes testdata/ruby/dbNetlist.rb | 21 +- 10 files changed, 482 insertions(+), 263 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index bf5de8610..4114a7e9f 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -46,7 +46,7 @@ Pin::Pin (const std::string &name) // Device class implementation Device::Device () - : mp_device_class (0), m_id (0) + : mp_device_class (0), m_id (0), mp_circuit (0) { // .. nothing yet .. } @@ -61,13 +61,13 @@ Device::~Device () } Device::Device (DeviceClass *device_class, const std::string &name) - : mp_device_class (device_class), m_name (name), m_id (0) + : mp_device_class (device_class), m_name (name), m_id (0), mp_circuit (0) { // .. nothing yet .. } Device::Device (const Device &other) - : mp_device_class (0), m_id (0) + : mp_device_class (0), m_id (0), mp_circuit (0) { operator= (other); } @@ -81,6 +81,11 @@ Device &Device::operator= (const Device &other) return *this; } +void Device::set_circuit (Circuit *circuit) +{ + mp_circuit = circuit; +} + void Device::set_name (const std::string &n) { m_name = n; @@ -176,7 +181,7 @@ void Device::set_parameter_value (const std::string &name, double v) // SubCircuit class implementation SubCircuit::SubCircuit () - : m_id (0) + : m_id (0), mp_circuit (0) { // .. nothing yet .. } @@ -191,13 +196,13 @@ SubCircuit::~SubCircuit() } SubCircuit::SubCircuit (Circuit *circuit, const std::string &name) - : m_circuit (circuit), m_name (name), m_id (0) + : m_circuit_ref (0), m_name (name), m_id (0), mp_circuit (0) { - // .. nothing yet .. + set_circuit_ref (circuit); } SubCircuit::SubCircuit (const SubCircuit &other) - : m_id (0) + : m_id (0), mp_circuit (0) { operator= (other); } @@ -206,8 +211,8 @@ SubCircuit &SubCircuit::operator= (const SubCircuit &other) { if (this != &other) { m_name = other.m_name; - m_circuit = other.m_circuit; m_trans = other.m_trans; + set_circuit_ref (const_cast (other.circuit_ref ())); } return *this; } @@ -230,6 +235,17 @@ void SubCircuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter) m_pin_refs [pin_id] = iter; } +void SubCircuit::set_circuit_ref (Circuit *c) +{ + if (m_circuit_ref.get ()) { + m_circuit_ref->unregister_ref (this); + } + m_circuit_ref.reset (c); + if (m_circuit_ref.get ()) { + m_circuit_ref->register_ref (this); + } +} + const Net *SubCircuit::net_for_pin (size_t pin_id) const { if (pin_id < m_pin_refs.size ()) { @@ -349,8 +365,8 @@ const Pin *NetPinRef::pin () const if (mp_net && mp_net->circuit ()) { return mp_net->circuit ()->pin_by_id (m_pin_id); } - } else if (mp_subcircuit->circuit ()) { - return mp_subcircuit->circuit ()->pin_by_id (m_pin_id); + } else if (mp_subcircuit->circuit_ref ()) { + return mp_subcircuit->circuit_ref ()->pin_by_id (m_pin_id); } return 0; } @@ -613,6 +629,8 @@ void Circuit::remove_net (Net *net) void Circuit::add_device (Device *device) { + device->set_circuit (this); + size_t id = 0; if (! m_devices.empty ()) { tl_assert (m_devices.back () != 0); @@ -658,6 +676,8 @@ Device *Circuit::device_by_id (size_t id) void Circuit::add_subcircuit (SubCircuit *subcircuit) { + subcircuit->set_circuit (this); + size_t id = 0; if (! m_subcircuits.empty ()) { tl_assert (m_subcircuits.back () != 0); @@ -701,12 +721,22 @@ SubCircuit *Circuit::subcircuit_by_id (size_t id) return d != m_subcircuit_id_table.end () ? d->second : 0; } +void Circuit::register_ref (SubCircuit *r) +{ + m_refs.push_back (r); +} + +void Circuit::unregister_ref (SubCircuit *r) +{ + m_refs.erase (r); +} + void Circuit::translate_circuits (const std::map &map) { for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) { - std::map::const_iterator m = map.find (i->circuit ()); + std::map::const_iterator m = map.find (i->circuit_ref ()); tl_assert (m != map.end ()); - i->set_circuit (m->second); + i->set_circuit_ref (m->second); } } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index cf44b1c84..2530ccc02 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -608,6 +608,24 @@ public: return m_id; } + /** + * @brief Gets the circuit the device lives in (const version) + * This pointer is 0 if the device isn't added to a circuit + */ + const Circuit *circuit () const + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the device lives in (non-const version) + * This pointer is 0 if the device isn't added to a circuit + */ + Circuit *circuit () + { + return mp_circuit; + } + /** * @brief Sets the name */ @@ -675,6 +693,7 @@ private: std::vector m_terminal_refs; std::vector m_parameters; size_t m_id; + Circuit *mp_circuit; /** * @brief Sets the terminal reference for a specific terminal @@ -696,6 +715,11 @@ private: { m_id = id; } + + /** + * @brief Sets the circuit + */ + void set_circuit (Circuit *circuit); }; /** @@ -727,7 +751,7 @@ public: /** * @brief Creates a subcircuit reference to the given circuit */ - SubCircuit (Circuit *circuit, const std::string &name = std::string ()); + SubCircuit (Circuit *circuit_ref, const std::string &name = std::string ()); /** * @brief Destructor @@ -746,19 +770,37 @@ public: } /** - * @brief Gets the circuit the reference points to (const version) + * @brief Gets the circuit the subcircuit lives in (const version) + * This pointer is 0 if the subcircuit isn't added to a circuit */ const Circuit *circuit () const { - return m_circuit.get (); + return mp_circuit; + } + + /** + * @brief Gets the circuit the subcircuit lives in (non-const version) + * This pointer is 0 if the subcircuit isn't added to a circuit + */ + Circuit *circuit () + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the reference points to (const version) + */ + const Circuit *circuit_ref () const + { + return m_circuit_ref.get (); } /** * @brief Gets the circuit the reference points to (non-const version) */ - Circuit *circuit () + Circuit *circuit_ref () { - return m_circuit.get (); + return m_circuit_ref.get (); } /** @@ -820,11 +862,12 @@ private: friend class Circuit; friend class Net; - tl::weak_ptr m_circuit; + tl::weak_ptr m_circuit_ref; std::string m_name; db::DCplxTrans m_trans; std::vector m_pin_refs; size_t m_id; + Circuit *mp_circuit; /** * @brief Sets the pin reference for a specific pin @@ -834,9 +877,14 @@ private: /** * @brief Sets the circuit reference */ + void set_circuit_ref (Circuit *c); + + /** + * @brief Sets the circuit the subcircuit belongs to + */ void set_circuit (Circuit *c) { - m_circuit.reset (c); + mp_circuit = c; } /** @@ -870,6 +918,8 @@ public: typedef tl::shared_collection subcircuit_list; typedef subcircuit_list::const_iterator const_subcircuit_iterator; typedef subcircuit_list::iterator subcircuit_iterator; + typedef tl::weak_collection::const_iterator const_refs_iterator; + typedef tl::weak_collection::iterator refs_iterator; /** * @brief Constructor @@ -939,9 +989,44 @@ public: return m_cell_index; } + /** + * @brief Gets the references to this circuit (begin, non-const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + refs_iterator begin_refs () + { + return m_refs.begin (); + } + + /** + * @brief Gets the references to this circuit (end, non-const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + refs_iterator end_refs () + { + return m_refs.end (); + } + + /** + * @brief Gets the references to this circuit (begin, const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + const_refs_iterator begin_refs () const + { + return m_refs.begin (); + } + + /** + * @brief Gets the references to this circuit (end, const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + const_refs_iterator end_refs () const + { + return m_refs.end (); + } + /** * @brief Adds a pin to this circuit - * * The circuit takes over ownership of the object. */ const Pin &add_pin(const Pin &pin); @@ -1200,6 +1285,7 @@ public: private: friend class Netlist; friend class Net; + friend class SubCircuit; std::string m_name; db::cell_index_type m_cell_index; @@ -1213,12 +1299,16 @@ private: std::map m_device_id_table; bool m_valid_subcircuit_id_table; std::map m_subcircuit_id_table; + tl::weak_collection m_refs; /** * @brief Sets the pin reference for a specific pin */ void set_pin_ref_for_pin (size_t ppin_id, Net::pin_iterator iter); + void register_ref (SubCircuit *sc); + void unregister_ref (SubCircuit *sc); + void translate_circuits (const std::map &map); void translate_device_classes (const std::map &map); void set_netlist (Netlist *netlist); diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 5664a0884..f0ce1a7f3 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -46,10 +46,10 @@ NetlistDeviceExtractorError::NetlistDeviceExtractorError (const std::string &cel // ---------------------------------------------------------------------------------------- // NetlistDeviceExtractor implementation -NetlistDeviceExtractor::NetlistDeviceExtractor () +NetlistDeviceExtractor::NetlistDeviceExtractor (const std::string &name) : mp_layout (0), m_cell_index (0), mp_circuit (0) { - m_device_name_index = 0; + m_name = name; m_propname_id = 0; } @@ -67,8 +67,7 @@ const tl::Variant &NetlistDeviceExtractor::terminal_property_name () void NetlistDeviceExtractor::initialize (db::Netlist *nl) { m_layer_definitions.clear (); - m_device_classes.clear (); - m_device_name_index = 0; + mp_device_class = 0; m_propname_id = 0; m_netlist.reset (nl); @@ -108,16 +107,16 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDevi } - extract_without_initialize (dss.layout (), dss.initial_cell (), layers, nl); + extract_without_initialize (dss.layout (), dss.initial_cell (), layers); } void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl) { initialize (nl); - extract_without_initialize (layout, cell, layers, nl); + extract_without_initialize (layout, cell, layers); } -void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl) +void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector &layers) { tl_assert (layers.size () == m_layer_definitions.size ()); @@ -216,9 +215,16 @@ void NetlistDeviceExtractor::extract_devices (const std::vector & /* void NetlistDeviceExtractor::register_device_class (DeviceClass *device_class) { + if (mp_device_class != 0) { + throw tl::Exception (tl::to_string (tr ("Device class already set"))); + } + + tl_assert (device_class != 0); + mp_device_class = device_class; + mp_device_class->set_name (m_name); + tl_assert (m_netlist.get () != 0); m_netlist->add_device_class (device_class); - m_device_classes.push_back (device_class); } void NetlistDeviceExtractor::define_layer (const std::string &name, const std::string &description) @@ -226,11 +232,14 @@ void NetlistDeviceExtractor::define_layer (const std::string &name, const std::s m_layer_definitions.push_back (db::NetlistDeviceExtractorLayerDefinition (name, description, m_layer_definitions.size ())); } -Device *NetlistDeviceExtractor::create_device (unsigned int device_class_index) +Device *NetlistDeviceExtractor::create_device () { + if (mp_device_class == 0) { + throw tl::Exception (tl::to_string (tr ("No device class registered"))); + } + tl_assert (mp_circuit != 0); - tl_assert (device_class_index < m_device_classes.size ()); - Device *device = new Device (m_device_classes[device_class_index], tl::to_string (++m_device_name_index)); + Device *device = new Device (mp_device_class); mp_circuit->add_device (device); return device; } diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index 5f4d458cf..871fc27a1 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -196,16 +196,24 @@ public: typedef std::map input_layers; /** - * @brief Default constructor + * @brief Constructor + * + * The name is the name of the device class of the devices generated. */ - NetlistDeviceExtractor (); + NetlistDeviceExtractor (const std::string &name); /** * @brief Destructor */ ~NetlistDeviceExtractor (); - // TODO: Do we need to declare input layers? + /** + * @brief Gets the name of the extractor and the device class + */ + const std::string &name () + { + return m_name; + } /** * @brief Gets the property name for the device terminal annotation @@ -292,8 +300,7 @@ protected: * defining the device classes and setting up the device layers. * * At least one device class needs to be defined. Use "register_device_class" to register - * the device classes you need. The first device class registered has device class index 0, - * the further ones 1, 2, etc. + * the device class you need. * * The device layers need to be defined by calling "define_layer" once or several times. */ @@ -323,7 +330,8 @@ protected: /** * @brief Registers a device class * The device class object will become owned by the netlist and must not be deleted by - * the caller. + * the caller. The name of the device class will be changed to the name given to + * the device extractor. * This method shall be used inside the implementation of "setup" to register * the device classes. */ @@ -343,7 +351,7 @@ protected: * The device object returned can be configured by the caller, e.g. set parameters. * It will be owned by the netlist and must not be deleted by the caller. */ - Device *create_device (unsigned int device_class_index = 0); + Device *create_device (); /** * @brief Defines a device terminal in the layout (a polygon) @@ -436,19 +444,23 @@ private: db::properties_id_type m_propname_id; db::cell_index_type m_cell_index; db::Circuit *mp_circuit; - std::vector m_device_classes; + db::DeviceClass *mp_device_class; + std::string m_name; layer_definitions m_layer_definitions; std::vector m_layers; - unsigned int m_device_name_index; error_list m_errors; + // no copying + NetlistDeviceExtractor (const NetlistDeviceExtractor &); + NetlistDeviceExtractor &operator= (const NetlistDeviceExtractor &); + /** * @brief Initializes the extractor * This method will produce the device classes required for the device extraction. */ void initialize (db::Netlist *nl); - void extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl); + void extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector &layers); }; } diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 32abccf50..126bff37c 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -51,6 +51,9 @@ Class decl_dbDevice ("db", "Device", gsi::method ("device_class", &db::Device::device_class, "@brief Gets the device class the device belongs to.\n" ) + + gsi::method ("circuit", (db::Circuit *(db::Device::*) ()) &db::Device::circuit, + "@brief Gets the circuit the device lives in." + ) + gsi::method ("id", &db::Device::id, "@brief Gets the device ID.\n" "The ID is a unique integer which identifies the device.\n" @@ -125,9 +128,13 @@ static void subcircuit_disconnect_pin1 (db::SubCircuit *subcircuit, const db::Pi } Class decl_dbSubCircuit ("db", "SubCircuit", - gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit, + gsi::method ("circuit_ref", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit_ref, "@brief Gets the circuit referenced by the subcircuit.\n" ) + + gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit, + "@brief Gets the circuit the subcircuit lives in.\n" + "This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \\circuit_ref." + ) + gsi::method ("id", &db::SubCircuit::id, "@brief Gets the subcircuit ID.\n" "The ID is a unique integer which identifies the subcircuit.\n" @@ -620,6 +627,9 @@ Class decl_dbCircuit ("db", "Circuit", "to the outside through such a pin. The pin is added after all existing " "pins. For more details see the \\Pin class." ) + + gsi::iterator ("each_ref", (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::begin_refs, (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::end_refs, + "@brief Iterates over the subcircuit objects referencing this circuit\n" + ) + gsi::iterator ("each_pin", (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::begin_pins, (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::end_pins, "@brief Iterates over the pins of the circuit" ) + diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index a9eaa5ec8..f8450667b 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -54,6 +54,7 @@ namespace { { public: DummyDeviceExtractor () + : db::NetlistDeviceExtractor (std::string ("DUMMY")) { error ("msg1"); error ("msg2", db::Box (0, 1, 2, 3)); diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index bbf7c72ab..14f6517c4 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -44,172 +44,6 @@ #include #include -class MOSFETExtractor - : public db::NetlistDeviceExtractor -{ -public: - MOSFETExtractor (db::Layout *debug_out) - : db::NetlistDeviceExtractor (), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) - { - if (mp_debug_out) { - m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); - m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); - } - } - - virtual void setup () - { - define_layer ("PD", "P diffusion"); - define_layer ("ND", "N diffusion"); - define_layer ("G", "Gate"); - define_layer ("P", "Poly"); - - db::DeviceClassMOS3Transistor *pmos_class = new db::DeviceClassMOS3Transistor (); - pmos_class->set_name ("PMOS"); - register_device_class (pmos_class); - - db::DeviceClassMOS3Transistor *nmos_class = new db::DeviceClassMOS3Transistor (); - nmos_class->set_name ("NMOS"); - register_device_class (nmos_class); - } - - virtual db::Connectivity get_connectivity (const db::Layout & /*layout*/, const std::vector &layers) const - { - tl_assert (layers.size () == 4); - - unsigned int lpdiff = layers [0]; - unsigned int lndiff = layers [1]; - unsigned int gate = layers [2]; - // not used for device recognition: poly (3), but used for producing the gate terminals - - // The layer definition is pdiff, ndiff, gate - db::Connectivity conn; - // collect all connected pdiff - conn.connect (lpdiff, lpdiff); - // collect all connected ndiff - conn.connect (lndiff, lndiff); - // collect all connected gate shapes - conn.connect (gate, gate); - // connect gate with pdiff - conn.connect (lpdiff, gate); - // connect gate with ndiff - conn.connect (lndiff, gate); - return conn; - } - - virtual void extract_devices (const std::vector &layer_geometry) - { - const db::Region &rpdiff = layer_geometry [0]; - const db::Region &rndiff = layer_geometry [1]; - const db::Region &rgates = layer_geometry [2]; - - for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) { - - db::Region rgate (*p); - db::Region rpdiff_on_gate = rpdiff.selected_interacting (rgate); - db::Region rndiff_on_gate = rndiff.selected_interacting (rgate); - - if (! rpdiff_on_gate.empty () && ! rndiff_on_gate.empty ()) { - error (tl::to_string (tr ("Gate shape touches both ndiff and pdiff - ignored")), *p); - } else if (rpdiff_on_gate.empty () && rndiff_on_gate.empty ()) { - error (tl::to_string (tr ("Gate shape touches neither ndiff and pdiff - ignored")), *p); - } else { - - bool is_pmos = ! rpdiff_on_gate.empty (); - - db::Region &diff = (is_pmos ? rpdiff_on_gate : rndiff_on_gate); - unsigned int terminal_geometry_index = (is_pmos ? 0 : 1); - unsigned int gate_geometry_index = 3; - unsigned int device_class_index = (is_pmos ? 0 /*PMOS*/ : 1 /*NMOS*/); - - if (diff.size () != 2) { - error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (diff.size ())), *p); - continue; - } - - db::Edges edges (rgate.edges () & diff.edges ()); - if (edges.size () != 2) { - error (tl::sprintf (tl::to_string (tr ("Expected two edges interacting gate/diff (found %d) - width and length may be incorrect")), int (edges.size ())), *p); - continue; - } - - if (! p->is_box ()) { - error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p); - } - - db::Device *device = create_device (device_class_index); - - device->set_parameter_value ("W", dbu () * edges.length () * 0.5); - device->set_parameter_value ("L", dbu () * (p->perimeter () - edges.length ()) * 0.5); - - int diff_index = 0; - for (db::Region::const_iterator d = diff.begin (); !d.at_end () && diff_index < 2; ++d, ++diff_index) { - - // count the number of gate shapes attached to this shape and distribute the area of the - // diffusion region to the number of gates - int n = rgates.selected_interacting (db::Region (*d)).size (); - tl_assert (n > 0); - - device->set_parameter_value (diff_index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n)); - - define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_geometry_index, *d); - - } - - define_terminal (device, device->device_class ()->terminal_id_for_name ("G"), gate_geometry_index, *p); - - // output the device for debugging - device_out (device, diff, rgate); - - } - - } - } - -private: - db::Layout *mp_debug_out; - unsigned int m_ldiff, m_lgate; - - void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) - { - if (! mp_debug_out) { - return; - } - - std::string cn = layout ()->cell_name (cell_index ()); - std::pair target_cp = mp_debug_out->cell_by_name (cn.c_str ()); - tl_assert (target_cp.first); - - db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->name ()).c_str ()); - mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ())); - - db::Cell &device_cell = mp_debug_out->cell (dci); - for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) { - device_cell.shapes (m_ldiff).insert (*p); - } - for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) { - device_cell.shapes (m_lgate).insert (*p); - } - - std::string ps; - const std::vector &pd = device->device_class ()->parameter_definitions (); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (! ps.empty ()) { - ps += ","; - } - ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ())); - } - device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ()))); - } -}; - -static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) -{ - unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); - lmap.map (ly.get_properties (lid), lid); - return lid; -} - static unsigned int layer_of (const db::Region ®ion) { return db::DeepLayer (region).layer (); @@ -248,6 +82,153 @@ static std::string pin_name (const db::Pin &pin) } } + +class MOSFETExtractor + : public db::NetlistDeviceExtractor +{ +public: + MOSFETExtractor (const std::string &name, db::Layout *debug_out) + : db::NetlistDeviceExtractor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) + { + if (mp_debug_out) { + m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); + m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); + } + } + + virtual void setup () + { + define_layer ("SD", "Source/drain diffusion"); + define_layer ("G", "Gate"); + define_layer ("P", "Poly"); + + register_device_class (new db::DeviceClassMOS3Transistor ()); + } + + virtual db::Connectivity get_connectivity (const db::Layout & /*layout*/, const std::vector &layers) const + { + tl_assert (layers.size () == 3); + + unsigned int diff = layers [0]; + unsigned int gate = layers [1]; + // not used for device recognition: poly (2), but used for producing the gate terminals + + // The layer definition is diff, gate + db::Connectivity conn; + // collect all connected diffusion shapes + conn.connect (diff, diff); + // collect all connected gate shapes + conn.connect (gate, gate); + // connect gate with diff to detect gate/diffusion boundary + conn.connect (diff, gate); + return conn; + } + + virtual void extract_devices (const std::vector &layer_geometry) + { + const db::Region &rdiff = layer_geometry [0]; + const db::Region &rgates = layer_geometry [1]; + + for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) { + + db::Region rgate (*p); + db::Region rdiff2gate = rdiff.selected_interacting (rgate); + + if (rdiff2gate.empty ()) { + error (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p); + } else { + + unsigned int terminal_geometry_index = 0; + unsigned int gate_geometry_index = 2; + + if (rdiff2gate.size () != 2) { + error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.size ())), *p); + continue; + } + + db::Edges edges (rgate.edges () & rdiff2gate.edges ()); + if (edges.size () != 2) { + error (tl::sprintf (tl::to_string (tr ("Expected two edges interacting gate/diff (found %d) - width and length may be incorrect")), int (edges.size ())), *p); + continue; + } + + if (! p->is_box ()) { + error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p); + } + + db::Device *device = create_device (); + + device->set_parameter_value ("W", dbu () * edges.length () * 0.5); + device->set_parameter_value ("L", dbu () * (p->perimeter () - edges.length ()) * 0.5); + + int diff_index = 0; + for (db::Region::const_iterator d = rdiff2gate.begin (); !d.at_end () && diff_index < 2; ++d, ++diff_index) { + + // count the number of gate shapes attached to this shape and distribute the area of the + // diffusion region to the number of gates + int n = rgates.selected_interacting (db::Region (*d)).size (); + tl_assert (n > 0); + + device->set_parameter_value (diff_index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n)); + + define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_geometry_index, *d); + + } + + define_terminal (device, device->device_class ()->terminal_id_for_name ("G"), gate_geometry_index, *p); + + // output the device for debugging + device_out (device, rdiff2gate, rgate); + + } + + } + } + +private: + db::Layout *mp_debug_out; + unsigned int m_ldiff, m_lgate; + + void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) + { + if (! mp_debug_out) { + return; + } + + std::string cn = layout ()->cell_name (cell_index ()); + std::pair target_cp = mp_debug_out->cell_by_name (cn.c_str ()); + tl_assert (target_cp.first); + + db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->circuit ()->name () + "_" + device_name (*device)).c_str ()); + mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ())); + + db::Cell &device_cell = mp_debug_out->cell (dci); + for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) { + device_cell.shapes (m_ldiff).insert (*p); + } + for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) { + device_cell.shapes (m_lgate).insert (*p); + } + + std::string ps; + const std::vector &pd = device->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (! ps.empty ()) { + ps += ","; + } + ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ())); + } + device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ()))); + } +}; + +static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) +{ + unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + // @@@ TODO: move this somewhere else static void dump_nets (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) @@ -329,14 +310,15 @@ static std::string netlist2string (const db::Netlist &nl) for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { std::string ps; const db::SubCircuit &subcircuit = *sc; - for (db::Circuit::const_pin_iterator p = sc->circuit ()->begin_pins (); p != sc->circuit ()->end_pins (); ++p) { - if (p != sc->circuit ()->begin_pins ()) { + const db::Circuit *circuit = sc->circuit_ref (); + for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) { + if (p != circuit->begin_pins ()) { ps += ","; } const db::Pin &pin = *p; ps += pin_name (pin) + "=" + net_name (subcircuit.net_for_pin (pin.id ())); } - res += std::string (" X") + sc->circuit ()->name () + " " + subcircuit_name (*sc) + " (" + ps + ")\n"; + res += std::string (" X") + circuit->name () + " " + subcircuit_name (*sc) + " (" + ps + ")\n"; } } @@ -344,7 +326,7 @@ static std::string netlist2string (const db::Netlist &nl) return res; } -TEST(2_DeviceAndNetExtraction) +TEST(1_DeviceAndNetExtraction) { db::Layout ly; db::LayerMap lmap; @@ -396,10 +378,14 @@ TEST(2_DeviceAndNetExtraction) db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss); // derived regions - db::Region rgate = ractive & rpoly; - db::Region rsd = ractive - rgate; - db::Region rpdiff = rsd & rnwell; - db::Region rndiff = rsd - rnwell; + + db::Region rpactive = ractive & rnwell; + db::Region rpgate = rpactive & rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = ractive - rnwell; + db::Region rngate = rnactive & rpoly; + db::Region rnsd = rnactive - rngate; // return the computed layers into the original layout and write it for debugging purposes @@ -408,10 +394,12 @@ TEST(2_DeviceAndNetExtraction) unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion - rgate.insert_into (&ly, tc.cell_index (), lgate); - rsd.insert_into (&ly, tc.cell_index (), lsd); - rpdiff.insert_into (&ly, tc.cell_index (), lpdiff); - rndiff.insert_into (&ly, tc.cell_index (), lndiff); + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); // perform the extraction @@ -420,15 +408,20 @@ TEST(2_DeviceAndNetExtraction) // NOTE: the device extractor will add more debug layers for the transistors: // 20/0 -> Diffusion // 21/0 -> Gate - MOSFETExtractor ex (&ly); + MOSFETExtractor pmos_ex ("PMOS", &ly); + MOSFETExtractor nmos_ex ("NMOS", &ly); db::NetlistDeviceExtractor::input_layers dl; - dl["PD"] = &rpdiff; - dl["ND"] = &rndiff; - dl["G"] = &rgate; - dl["P"] = &rpoly; - ex.extract (dss, dl, &nl); + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + pmos_ex.extract (dss, dl, &nl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + nmos_ex.extract (dss, dl, &nl); // perform the net extraction @@ -436,8 +429,8 @@ TEST(2_DeviceAndNetExtraction) db::Connectivity conn; // Intra-layer - conn.connect (rpdiff); - conn.connect (rndiff); + conn.connect (rpsd); + conn.connect (rnsd); conn.connect (rpoly); conn.connect (rdiff_cont); conn.connect (rpoly_cont); @@ -445,8 +438,8 @@ TEST(2_DeviceAndNetExtraction) conn.connect (rvia1); conn.connect (rmetal2); // Inter-layer - conn.connect (rpdiff, rdiff_cont); - conn.connect (rndiff, rdiff_cont); + conn.connect (rpsd, rdiff_cont); + conn.connect (rnsd, rdiff_cont); conn.connect (rpoly, rpoly_cont); conn.connect (rpoly_cont, rmetal1); conn.connect (rdiff_cont, rmetal1); @@ -468,9 +461,11 @@ TEST(2_DeviceAndNetExtraction) // 206/0 -> Metal1 // 207/0 -> Via1 // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain std::map dump_map; - dump_map [layer_of (rpdiff) ] = ly.insert_layer (db::LayerProperties (210, 0)); - dump_map [layer_of (rndiff) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0)); dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); @@ -496,10 +491,10 @@ TEST(2_DeviceAndNetExtraction) " XINV2 $9 ($1=$I6,$2=$I45,$3=$I7,$4=VSS,$5=VDD)\n" " XINV2 $10 ($1=$I7,$2=$I46,$3=$I8,$4=VSS,$5=VDD)\n" "Circuit INV2 ($1=IN,$2=$2,$3=OUT,$4=$4,$5=$5):\n" - " DPMOS 1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS 2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS 3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS 4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" @@ -523,19 +518,9 @@ TEST(2_DeviceAndNetExtraction) // An attempt to simplify things. /* -- layers: use db::Region, or wrapper? --> use regions, but test whether they are deep regions (?) - TODO: - netlist query functions such as net_by_name, device_by_name, circuit_by_name - terminal geometry (Polygon) for device, combined device geometry (all terminals) -- error interface for device extraction - // gets the device extraction errors - // device_extraction_error_iterator begin_device_extraction_errors () const; - // device_extraction_error_iterator end_device_extraction_errors () const; - // bool has_device_extraction_errors () const; - -- device extractor needs to declare the layers to allow passing them by name - netlist manipulation methods (i.e. flatten certain cells, purging etc.) */ @@ -551,23 +536,25 @@ public: // the iterator provides the hierarchical selection (enabling/disabling cells etc.) LayoutToNetlist (const db::RecursiveShapeIterator &iter); - // --- preparation + // --- Step 0: configuration - // returns a new'd region + void set_threads (unsigned int n); + void set_area_ratio (double ar); + void set_max_vertex_count (size_t n); + + // --- Step 1: preparation + + // returns new'd regions db::Region *make_layer (unsigned int layer_index); db::Region *make_text_layer (unsigned int layer_index); db::Region *make_polygon_layer (unsigned int layer_index); - // gets the internal layout and cell - const db::Layout &internal_layout () const; - const db::Cell &internal_top_cell () const; - - // --- device extraction + // --- Step 2: device extraction // after this, the device extractor will have errors if some occured. - void extract_devices (db::NetlistDeviceExtractor *extractor, const std::map &layers); + void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers); - // --- net extraction + // --- Step 3: net extraction // define connectivity for the netlist extraction void connect (const db::Region &l); @@ -576,7 +563,11 @@ public: // runs the netlist extraction void extract_netlist (); - // --- retrieval + // --- Step 4: retrieval + + // gets the internal layout and cell (0 if not available) + const db::Layout *internal_layout () const; + const db::Cell *internal_top_cell () const; // gets the internal layer index of the given region unsigned int layer_of (const db::Region ®ion) const; @@ -600,6 +591,11 @@ public: // copies the shapes of the given net from a given layer // (recursive true: include nets from subcircuits) db::Region shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive); + + // finds the net by probing a specific location on the given layer looking through the + // hierarchy. Returns 0 if no net was found. + db::Net *probe_net (const db::Region &of_region, const db::DPoint &point); + db::Net *probe_net (const db::Region &of_region, const db::Point &point); }; } diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index e1df6d1df..9a2a172d4 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -165,7 +165,7 @@ static std::string net2string (const db::Net &n) res += ","; } if (i->subcircuit ()) { - res += i->subcircuit ()->circuit () ? i->subcircuit ()->circuit ()->name () : "(null)"; + res += i->subcircuit ()->circuit_ref () ? i->subcircuit ()->circuit_ref ()->name () : "(null)"; res += ":"; } else { res += "+"; @@ -221,15 +221,15 @@ static std::string netlist2 (const db::Circuit &c) } for (db::Circuit::const_subcircuit_iterator s = c.begin_subcircuits (); s != c.end_subcircuits (); ++s) { - if (! s->circuit ()) { + if (! s->circuit_ref ()) { continue; } pins.clear (); - for (size_t i = 0; i < s->circuit ()->pin_count (); ++i) { + for (size_t i = 0; i < s->circuit_ref ()->pin_count (); ++i) { if (! pins.empty ()) { pins += ","; } - pins += s->circuit ()->pin_by_id (i)->name (); + pins += s->circuit_ref ()->pin_by_id (i)->name (); pins += "="; const db::Net *net = s->net_for_pin (i); pins += net ? net->name () : std::string ("(null)"); @@ -268,7 +268,11 @@ TEST(4_CircuitDevices) db::Device *d1 = new db::Device (&dc1, "d1"); db::Device *d2a = new db::Device (&dc2, "d2a"); db::Device *d2b = new db::Device (&dc2, "d2b"); + + EXPECT_EQ (d1->circuit () == 0, true); + c->add_device (d1); + EXPECT_EQ (d1->circuit () == c.get (), true); EXPECT_EQ (d1->id (), size_t (1)); EXPECT_EQ (c->device_by_id (d1->id ()) == d1, true); c->add_device (dd); @@ -386,6 +390,18 @@ static std::string netlist2 (const db::Netlist &nl) return res; } +static std::string refs2string (const db::Circuit *c) +{ + std::string res; + for (db::Circuit::const_refs_iterator r = c->begin_refs (); r != c->end_refs (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += r->name (); + } + return res; +} + TEST(4_NetlistSubcircuits) { std::auto_ptr nl (new db::Netlist ()); @@ -413,14 +429,19 @@ TEST(4_NetlistSubcircuits) db::Device *d = new db::Device (dc, "D"); c2->add_device (d); + EXPECT_EQ (refs2string (c2), ""); db::SubCircuit *sc1 = new db::SubCircuit (c2); sc1->set_name ("sc1"); + EXPECT_EQ (refs2string (c2), "sc1"); + EXPECT_EQ (sc1->circuit () == 0, true); c1->add_subcircuit (sc1); + EXPECT_EQ (sc1->circuit () == c1, true); EXPECT_EQ (sc1->id (), size_t (1)); EXPECT_EQ (c1->subcircuit_by_id (sc1->id ()) == sc1, true); db::SubCircuit *sc2 = new db::SubCircuit (c2); sc2->set_name ("sc2"); + EXPECT_EQ (refs2string (c2), "sc1,sc2"); c1->add_subcircuit (sc2); EXPECT_EQ (sc2->id (), size_t (2)); EXPECT_EQ (c1->subcircuit_by_id (sc2->id ()) == sc2, true); @@ -778,3 +799,38 @@ TEST(10_NetPinRefBasics) EXPECT_EQ (db::NetPinRef (&d1, 1) < db::NetPinRef (&d1, 0), false); EXPECT_NE ((db::NetPinRef (&d1, 0) < db::NetPinRef (&d2, 0)), (db::NetPinRef (&d2, 0) < db::NetPinRef (&d1, 0))); } + +TEST(11_NetlistCircuitRefs) +{ + std::auto_ptr nl (new db::Netlist ()); + + db::Circuit *c1 = new db::Circuit (); + c1->set_name ("c1"); + nl->add_circuit (c1); + + db::Circuit *c2 = new db::Circuit (); + c2->set_name ("c2"); + nl->add_circuit (c2); + + db::SubCircuit *sc1 = new db::SubCircuit (c2); + sc1->set_name ("sc1"); + EXPECT_EQ (refs2string (c2), "sc1"); + c1->add_subcircuit (sc1); + + db::SubCircuit *sc2 = new db::SubCircuit (c2); + sc2->set_name ("sc2"); + EXPECT_EQ (refs2string (c2), "sc1,sc2"); + c1->add_subcircuit (sc2); + + *sc2 = db::SubCircuit (); + EXPECT_EQ (refs2string (c2), "sc1"); + + db::SubCircuit sc3 (c2); + sc3.set_name ("sc3"); + *sc2 = sc3; + sc2->set_name ("sc2"); + EXPECT_EQ (refs2string (c2), "sc1,sc3,sc2"); + + sc3 = db::SubCircuit (); + EXPECT_EQ (refs2string (c2), "sc1,sc2"); +} diff --git a/testdata/algo/device_extract_au1.gds b/testdata/algo/device_extract_au1.gds index 05c3b013fd10004443e7780adcea823a917aa686..a970a662c27bcbfaccc543ce1258a0205de3decc 100644 GIT binary patch delta 758 zcmX@+vdvA2fsKKQDS|fEfwahD03k(;HsV;PmPc!@!P zjm^*3KRDjgFU%-j#bh!YpVMRxKB37kcn&HNR=@cuuNY2q0}$pKP0kW!8lgz#zklGx0KTv9Set`nmg07L*s>d_k^^831QsiZB2G delta 964 zcma)4Jx{_=6uqsbKm%wkZ6cJSji4+he#D7H27_?`RANF%i2uN`lO!g@!NGytjm5-~ zwTX#NxG?DAZde>i_yZ(T`}#y29;wrL_uTvL$H{pt_76=Ef#++WbA0dFSr#xQih6GI#x@tD@ zT8@XvgAmx$6KKh?yeq8NfFS+&WqCgY89)dE=z+NoLrb}5s<;_XF}KH>+73(0qNl<( z7BuxY+96TM&3P*0fu@m^7wqA?wr1aGp#VmW5++i+STdrnG5p+zd1FhP*^ZV^Mg*~t nE~1!T_tMmD^!mw&%45{4{?7ZaP{ciF$K|66a;u8nOIr)yj-=*U diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index eeead78df..04249daab 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -258,22 +258,35 @@ class DBNetlist_TestClass < TestBase sc1 = c.create_subcircuit(cc) sc1.name = "SC1" + assert_equal(sc1.circuit.object_id, c.object_id) assert_equal(sc1.name, "SC1") - assert_equal(sc1.circuit.name, "CC") + assert_equal(sc1.circuit_ref.name, "CC") assert_equal(c.subcircuit_by_id(sc1.id).id, 1) assert_equal(c.subcircuit_by_id(2).inspect, "nil") + refs = [] + cc.each_ref { |r| refs << r.name } + assert_equal(refs.join(","), "SC1") + sc2 = c.create_subcircuit(cc) sc2.name = "SC2" + refs = [] + cc.each_ref { |r| refs << r.name } + assert_equal(refs.join(","), "SC1,SC2") + names = [] ccn = [] - c.each_subcircuit { |sc| names << sc.name; ccn << sc.circuit.name } + c.each_subcircuit { |sc| names << sc.name; ccn << sc.circuit_ref.name } assert_equal(names, [ "SC1", "SC2" ]) assert_equal(ccn, [ "CC", "CC" ]) c.remove_subcircuit(sc2) + refs = [] + cc.each_ref { |r| refs << r.name } + assert_equal(refs.join(","), "SC1") + names = [] c.each_subcircuit { |sc| names << sc.name} assert_equal(names, [ "SC1" ]) @@ -282,7 +295,7 @@ class DBNetlist_TestClass < TestBase names = [] ccn = [] - c.each_subcircuit { |sc| names << sc.name; ccn << sc.circuit.name } + c.each_subcircuit { |sc| names << sc.name; ccn << sc.circuit_ref.name } assert_equal(names, [ "SC1", "SC2" ]) assert_equal(ccn, [ "CC", "CC" ]) @@ -531,6 +544,8 @@ class DBNetlist_TestClass < TestBase nl.add(c) d1 = c.create_device(dc) + assert_equal(d1.circuit.object_id, c.object_id) + assert_equal(d1.parameter(0), 1.0) assert_equal(d1.parameter("U"), 1.0) assert_equal(d1.parameter(1), 2.0) From d57ede441c3d24dc54f128052e991177f4993b77 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Dec 2018 00:48:28 +0100 Subject: [PATCH 140/335] WIP: netlist topology - children, parents, top-down and bottom-up iteration. --- src/db/db/dbNetlist.cc | 285 +++++++++++++++++++++++++++- src/db/db/dbNetlist.h | 144 +++++++++++++- src/db/db/gsiDeclDbNetlist.cc | 23 +++ src/db/unit_tests/dbNetlistTests.cc | 118 ++++++++++++ testdata/ruby/dbNetlist.rb | 92 +++++++++ 5 files changed, 655 insertions(+), 7 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 4114a7e9f..0e954a4e4 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -504,13 +504,13 @@ void Net::set_circuit (Circuit *circuit) // Circuit class implementation Circuit::Circuit () - : mp_netlist (0), m_valid_device_id_table (false), m_valid_subcircuit_id_table (false) + : mp_netlist (0), m_valid_device_id_table (false), m_valid_subcircuit_id_table (false), m_index (0) { // .. nothing yet .. } Circuit::Circuit (const Circuit &other) - : mp_netlist (0), m_valid_device_id_table (false), m_valid_subcircuit_id_table (false) + : mp_netlist (0), m_valid_device_id_table (false), m_valid_subcircuit_id_table (false), m_index (0) { operator= (other); } @@ -609,6 +609,54 @@ void Circuit::set_cell_index (const db::cell_index_type ci) m_cell_index = ci; } +Circuit::child_circuit_iterator Circuit::begin_children () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->child_circuits (this).begin (); +} + +Circuit::child_circuit_iterator Circuit::end_children () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->child_circuits (this).end (); +} + +Circuit::const_child_circuit_iterator Circuit::begin_children () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->child_circuits (const_cast (this))).begin (); +} + +Circuit::const_child_circuit_iterator Circuit::end_children () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->child_circuits (const_cast (this))).end (); +} + +Circuit::child_circuit_iterator Circuit::begin_parents () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->parent_circuits (this).begin (); +} + +Circuit::child_circuit_iterator Circuit::end_parents () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->parent_circuits (this).end (); +} + +Circuit::const_child_circuit_iterator Circuit::begin_parents () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->parent_circuits (const_cast (this))).begin (); +} + +Circuit::const_child_circuit_iterator Circuit::end_parents () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->parent_circuits (const_cast (this))).end (); +} + const Pin &Circuit::add_pin (const Pin &pin) { m_pins.push_back (pin); @@ -687,12 +735,20 @@ void Circuit::add_subcircuit (SubCircuit *subcircuit) m_subcircuits.push_back (subcircuit); invalidate_subcircuit_id_table (); + + if (mp_netlist) { + mp_netlist->invalidate_topology (); + } } void Circuit::remove_subcircuit (SubCircuit *subcircuit) { m_subcircuits.erase (subcircuit); invalidate_subcircuit_id_table (); + + if (mp_netlist) { + mp_netlist->invalidate_topology (); + } } void Circuit::validate_subcircuit_id_table () @@ -1073,21 +1129,25 @@ size_t DeviceClass::terminal_id_for_name (const std::string &name) const // Netlist class implementation Netlist::Netlist () + : m_valid_topology (false) { - // .. nothing yet .. + m_circuits.changed ().add (this, &Netlist::invalidate_topology); } Netlist::Netlist (const Netlist &other) + : m_valid_topology (false) { operator= (other); + m_circuits.changed ().add (this, &Netlist::invalidate_topology); } Netlist &Netlist::operator= (const Netlist &other) { if (this != &other) { + clear (); + std::map dct; - m_device_classes.clear (); for (const_device_class_iterator dc = other.begin_device_classes (); dc != other.end_device_classes (); ++dc) { DeviceClass *dc_new = dc->clone (); dct [dc.operator-> ()] = dc_new; @@ -1110,6 +1170,223 @@ Netlist &Netlist::operator= (const Netlist &other) return *this; } +void Netlist::invalidate_topology () +{ + if (m_valid_topology) { + m_valid_topology = false; + m_top_circuits = 0; + m_top_down_circuits.clear (); + m_child_circuits.clear (); + m_parent_circuits.clear (); + } +} + +namespace { + struct sort_by_index + { + bool operator () (const Circuit *a, const Circuit *b) const + { + return a->index () < b->index (); + } + }; +} + +void Netlist::validate_topology () +{ + if (m_valid_topology) { + return; + } + + m_child_circuits.clear (); + m_parent_circuits.clear (); + + size_t max_index = 0; + for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + c->set_index (max_index); + ++max_index; + } + + // build the child circuit list ... needed for the topology sorting + + m_child_circuits.reserve (max_index); + m_parent_circuits.reserve (max_index); + + for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + + std::set children; + for (Circuit::subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { + if (sc->circuit_ref ()) { + children.insert (sc->circuit_ref ()); + } + } + + m_child_circuits.push_back (tl::vector ()); + tl::vector &cc = m_child_circuits.back (); + cc.reserve (children.size ()); + cc.insert (cc.end (), children.begin (), children.end ()); + // sort by index for better reproducibility + std::sort (cc.begin (), cc.end (), sort_by_index ()); + + std::set parents; + for (Circuit::refs_iterator sc = c->begin_refs (); sc != c->end_refs (); ++sc) { + if (sc->circuit ()) { + parents.insert (sc->circuit ()); + } + } + + m_parent_circuits.push_back (tl::vector ()); + tl::vector &pc = m_parent_circuits.back (); + pc.reserve (parents.size ()); + pc.insert (pc.end (), parents.begin (), parents.end ()); + // sort by index for better reproducibility + std::sort (pc.begin (), pc.end (), sort_by_index ()); + + } + + // do topology sorting + + m_top_circuits = 0; + m_top_down_circuits.clear (); + m_top_down_circuits.reserve (max_index); + + std::vector num_parents (max_index, 0); + + // while there are cells to treat .. + while (m_top_down_circuits.size () != max_index) { + + size_t n_top_down_circuits = m_top_down_circuits.size (); + + // Treat all circuits that do not have all parents reported. + // For all such a circuits, disable the parent counting, + // add the circuits's index to the top-down sorted list and + // increment the reported parent count in all the + // child circuits. + + for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + if (m_parent_circuits [c->index ()].size () == num_parents [c->index ()]) { + m_top_down_circuits.push_back (c.operator-> ()); + num_parents [c->index ()] = std::numeric_limits::max (); + } + } + + // For all these a circuits, increment the reported parent instance + // count in all the child circuits. + for (tl::vector::const_iterator ii = m_top_down_circuits.begin () + n_top_down_circuits; ii != m_top_down_circuits.end (); ++ii) { + const tl::vector &cc = m_child_circuits [(*ii)->index ()]; + for (tl::vector::const_iterator icc = cc.begin (); icc != cc.end (); ++icc) { + tl_assert (num_parents [(*icc)->index ()] != std::numeric_limits::max ()); + num_parents [(*icc)->index ()] += 1; + } + } + + // If no new cells have been reported this is basically a + // sign of recursion in the graph. + if (n_top_down_circuits == m_top_down_circuits.size ()) { + throw tl::Exception (tl::to_string (tr ("Recursive hierarchy detected in netlist"))); + } + + } + + // Determine the number of top cells + for (tl::vector::const_iterator e = m_top_down_circuits.begin (); e != m_top_down_circuits.end () && m_parent_circuits [(*e)->index ()].empty (); ++e) { + ++m_top_circuits; + } + + m_valid_topology = true; +} + +const tl::vector &Netlist::child_circuits (Circuit *circuit) +{ + if (! m_valid_topology) { + validate_topology (); + } + + tl_assert (circuit->index () < m_child_circuits.size ()); + return m_child_circuits [circuit->index ()]; +} + +const tl::vector &Netlist::parent_circuits (Circuit *circuit) +{ + if (! m_valid_topology) { + validate_topology (); + } + + tl_assert (circuit->index () < m_parent_circuits.size ()); + return m_parent_circuits [circuit->index ()]; +} + +Netlist::top_down_circuit_iterator Netlist::begin_top_down () +{ + if (! m_valid_topology) { + validate_topology (); + } + return m_top_down_circuits.begin (); +} + +Netlist::top_down_circuit_iterator Netlist::end_top_down () +{ + if (! m_valid_topology) { + validate_topology (); + } + return m_top_down_circuits.end (); +} + +Netlist::const_top_down_circuit_iterator Netlist::begin_top_down () const +{ + if (! m_valid_topology) { + const_cast (this)->validate_topology (); + } + return reinterpret_cast &> (m_top_down_circuits).begin (); +} + +Netlist::const_top_down_circuit_iterator Netlist::end_top_down () const +{ + if (! m_valid_topology) { + const_cast (this)->validate_topology (); + } + return reinterpret_cast &> (m_top_down_circuits).end (); +} + +size_t Netlist::top_circuit_count () const +{ + if (! m_valid_topology) { + const_cast (this)->validate_topology (); + } + return m_top_circuits; +} + +Netlist::bottom_up_circuit_iterator Netlist::begin_bottom_up () +{ + if (! m_valid_topology) { + validate_topology (); + } + return m_top_down_circuits.rbegin (); +} + +Netlist::bottom_up_circuit_iterator Netlist::end_bottom_up () +{ + if (! m_valid_topology) { + validate_topology (); + } + return m_top_down_circuits.rend (); +} + +Netlist::const_bottom_up_circuit_iterator Netlist::begin_bottom_up () const +{ + if (! m_valid_topology) { + const_cast (this)->validate_topology (); + } + return reinterpret_cast &> (m_top_down_circuits).rbegin (); +} + +Netlist::const_bottom_up_circuit_iterator Netlist::end_bottom_up () const +{ + if (! m_valid_topology) { + const_cast (this)->validate_topology (); + } + return reinterpret_cast &> (m_top_down_circuits).rend (); +} + void Netlist::clear () { m_device_classes.clear (); diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 2530ccc02..cbdedddf8 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -920,6 +920,10 @@ public: typedef subcircuit_list::iterator subcircuit_iterator; typedef tl::weak_collection::const_iterator const_refs_iterator; typedef tl::weak_collection::iterator refs_iterator; + typedef tl::vector::const_iterator child_circuit_iterator; + typedef tl::vector::const_iterator const_child_circuit_iterator; + typedef tl::vector::const_iterator parent_circuit_iterator; + typedef tl::vector::const_iterator const_parent_circuit_iterator; /** * @brief Constructor @@ -974,6 +978,15 @@ public: return m_name; } + /** + * @brief The index of the circuit in the netlist + * CAUTION: this attribute is used for internal purposes and may not be valid always. + */ + size_t index () const + { + return m_index; + } + /** * @brief Sets the layout cell reference for this circuit * @@ -1016,6 +1029,52 @@ public: return m_refs.begin (); } + /** + * @brief Gets the child circuits iterator (begin) + * The child circuits are the circuits referenced by all subcircuits + * in the circuit. + */ + child_circuit_iterator begin_children (); + + /** + * @brief Gets the child circuits iterator (end) + */ + child_circuit_iterator end_children (); + + /** + * @brief Gets the child circuits iterator (begin, const version) + * The child circuits are the circuits referenced by all subcircuits + * in the circuit. + */ + const_child_circuit_iterator begin_children () const; + + /** + * @brief Gets the child circuits iterator (end, const version) + */ + const_child_circuit_iterator end_children () const; + + /** + * @brief Gets the parent circuits iterator (begin) + * The parents circuits are the circuits referencing this circuit via subcircuits. + */ + parent_circuit_iterator begin_parents (); + + /** + * @brief Gets the parent circuits iterator (end) + */ + parent_circuit_iterator end_parents (); + + /** + * @brief Gets the parent circuits iterator (begin, const version) + * The parents circuits are the circuits referencing this circuit via subcircuits. + */ + const_parent_circuit_iterator begin_parents () const; + + /** + * @brief Gets the parent circuits iterator (end, const version) + */ + const_parent_circuit_iterator end_parents () const; + /** * @brief Gets the references to this circuit (end, const version) * This iterator will deliver all subcircuits referencing this circuit @@ -1300,10 +1359,13 @@ private: bool m_valid_subcircuit_id_table; std::map m_subcircuit_id_table; tl::weak_collection m_refs; + size_t m_index; + + void set_index (size_t index) + { + m_index = index; + } - /** - * @brief Sets the pin reference for a specific pin - */ void set_pin_ref_for_pin (size_t ppin_id, Net::pin_iterator iter); void register_ref (SubCircuit *sc); @@ -1717,6 +1779,10 @@ public: typedef tl::shared_collection device_class_list; typedef device_class_list::const_iterator const_device_class_iterator; typedef device_class_list::iterator device_class_iterator; + typedef tl::vector::const_iterator top_down_circuit_iterator; + typedef tl::vector::const_iterator const_top_down_circuit_iterator; + typedef tl::vector::const_reverse_iterator bottom_up_circuit_iterator; + typedef tl::vector::const_reverse_iterator const_bottom_up_circuit_iterator; /** * @brief Constructor @@ -1784,6 +1850,65 @@ public: return m_circuits.end (); } + /** + * @brief Gets the top-down circuits iterator (begin) + * This iterator will deliver the circuits in a top-down way - i.e. child circuits + * will always come after parent circuits. + * The first "top_circuit_count" elements will be top circuits (those which are not + * referenced by other circuits). + */ + top_down_circuit_iterator begin_top_down (); + + /** + * @brief Gets the top-down circuits iterator (end) + */ + top_down_circuit_iterator end_top_down (); + + /** + * @brief Gets the top-down circuits iterator (begin, const version) + * This iterator will deliver the circuits in a top-down way - i.e. child circuits + * will always come after parent circuits. + * The first "top_circuit_count" elements will be top circuits (those which are not + * referenced by other circuits). + */ + const_top_down_circuit_iterator begin_top_down () const; + + /** + * @brief Gets the top-down circuits iterator (end, const version) + */ + const_top_down_circuit_iterator end_top_down () const; + + /** + * @brief Gets the number of top circuits + * Top circuits are those which are not referenced by other circuits. + * In a well-formed netlist there is a single top-level circuit. + */ + size_t top_circuit_count () const; + + /** + * @brief Gets the bottom-up circuits iterator (begin) + * This iterator will deliver the circuits in a bottom-up way - i.e. child circuits + * will always come before parent circuits. + */ + bottom_up_circuit_iterator begin_bottom_up (); + + /** + * @brief Gets the bottom-up circuits iterator (end) + */ + bottom_up_circuit_iterator end_bottom_up (); + + /** + * @brief Gets the bottom-up circuits iterator (begin, const version) + * This iterator will deliver the circuits in a bottom-up way - i.e. child circuits + * will always come before parent circuits. + */ + const_bottom_up_circuit_iterator begin_bottom_up () const; + + /** + * @brief Gets the bottom-up circuits iterator (end, const version) + */ + const_bottom_up_circuit_iterator end_bottom_up () const; + /** * @brief Adds a device class to this netlist * @@ -1844,8 +1969,21 @@ public: void combine_devices (); private: + friend class Circuit; + circuit_list m_circuits; device_class_list m_device_classes; + bool m_valid_topology; + tl::vector m_top_down_circuits; + tl::vector > m_child_circuits; + tl::vector > m_parent_circuits; + size_t m_top_circuits; + + void invalidate_topology (); + void validate_topology (); + + const tl::vector &child_circuits (Circuit *circuit); + const tl::vector &parent_circuits (Circuit *circuit); }; } diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 126bff37c..ea9b8c71c 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -627,6 +627,14 @@ Class decl_dbCircuit ("db", "Circuit", "to the outside through such a pin. The pin is added after all existing " "pins. For more details see the \\Pin class." ) + + gsi::iterator ("each_child", (db::Circuit::child_circuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_children, (db::Circuit::child_circuit_iterator (db::Circuit::*) ()) &db::Circuit::end_children, + "@brief Iterates over the child circuits of this circuit\n" + "Child circuits are the ones that are referenced from this circuit via subcircuits." + ) + + gsi::iterator ("each_parent", (db::Circuit::parent_circuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_parents, (db::Circuit::parent_circuit_iterator (db::Circuit::*) ()) &db::Circuit::end_parents, + "@brief Iterates over the parent circuits of this circuit\n" + "Child circuits are the ones that are referencing this circuit via subcircuits." + ) + gsi::iterator ("each_ref", (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::begin_refs, (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::end_refs, "@brief Iterates over the subcircuit objects referencing this circuit\n" ) + @@ -800,6 +808,21 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Removes the given circuit object from the netlist\n" "After the object has been removed, it becomes invalid and cannot be used further." ) + + gsi::iterator ("each_circuit_top_down", (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_top_down, (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_top_down, + "@brief Iterates over the circuits top-down\n" + "Iterating top-down means the parent circuits come before the child circuits. " + "The first \\top_circuit_count circuits are top circuits - i.e. those which are not referenced by other circuits." + ) + + gsi::iterator ("each_circuit_bottom_up", (db::Netlist::bottom_up_circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_bottom_up, (db::Netlist::bottom_up_circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_bottom_up, + "@brief Iterates over the circuits bottom-up\n" + "Iterating bottom-up means the parent circuits come after the child circuits. " + "This is the basically the reverse order as delivered by \\each_circuit_top_down." + ) + + gsi::method ("top_circuit_count", &db::Netlist::top_circuit_count, + "@brief Gets the number of top circuits.\n" + "Top circuits are those which are not referenced by other circuits via subcircuits. " + "A well-formed netlist has a single top circuit." + ) + gsi::iterator ("each_circuit", (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_circuits, (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_circuits, "@brief Iterates over the circuits of the netlist" ) + diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 9a2a172d4..3edfe1503 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -834,3 +834,121 @@ TEST(11_NetlistCircuitRefs) sc3 = db::SubCircuit (); EXPECT_EQ (refs2string (c2), "sc1,sc2"); } + +static std::string children2string (const db::Circuit *c) +{ + std::string res; + for (db::Circuit::const_child_circuit_iterator r = c->begin_children (); r != c->end_children (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +static std::string parents2string (const db::Circuit *c) +{ + std::string res; + for (db::Circuit::const_parent_circuit_iterator r = c->begin_parents (); r != c->end_parents (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +static std::string td2string (const db::Netlist *nl) +{ + std::string res; + for (db::Netlist::const_top_down_circuit_iterator r = nl->begin_top_down (); r != nl->end_top_down (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +static std::string bu2string (const db::Netlist *nl) +{ + std::string res; + for (db::Netlist::const_bottom_up_circuit_iterator r = nl->begin_bottom_up (); r != nl->end_bottom_up (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +TEST(12_NetlistTopology) +{ + std::auto_ptr nl (new db::Netlist ()); + EXPECT_EQ (nl->top_circuit_count (), size_t (0)); + + db::Circuit *c1 = new db::Circuit (); + c1->set_name ("c1"); + nl->add_circuit (c1); + EXPECT_EQ (nl->top_circuit_count (), size_t (1)); + EXPECT_EQ (td2string (nl.get ()), "c1"); + EXPECT_EQ (bu2string (nl.get ()), "c1"); + + db::Circuit *c2 = new db::Circuit (); + c2->set_name ("c2"); + nl->add_circuit (c2); + EXPECT_EQ (nl->top_circuit_count (), size_t (2)); + EXPECT_EQ (td2string (nl.get ()), "c1,c2"); + EXPECT_EQ (bu2string (nl.get ()), "c2,c1"); + + db::Circuit *c3 = new db::Circuit (); + c3->set_name ("c3"); + nl->add_circuit (c3); + EXPECT_EQ (nl->top_circuit_count (), size_t (3)); + EXPECT_EQ (td2string (nl.get ()), "c1,c2,c3"); + EXPECT_EQ (bu2string (nl.get ()), "c3,c2,c1"); + + db::SubCircuit *sc1 = new db::SubCircuit (c2); + sc1->set_name ("sc1"); + c1->add_subcircuit (sc1); + EXPECT_EQ (children2string (c1), "c2"); + EXPECT_EQ (parents2string (c2), "c1"); + EXPECT_EQ (nl->top_circuit_count (), size_t (2)); + EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2"); + EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1"); + + db::SubCircuit *sc2 = new db::SubCircuit (c2); + sc2->set_name ("sc2"); + c1->add_subcircuit (sc2); + EXPECT_EQ (children2string (c1), "c2"); + EXPECT_EQ (parents2string (c2), "c1"); + EXPECT_EQ (nl->top_circuit_count (), size_t (2)); + EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2"); + EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1"); + + db::SubCircuit *sc3 = new db::SubCircuit (c3); + sc3->set_name ("sc3"); + c1->add_subcircuit (sc3); + EXPECT_EQ (children2string (c1), "c2,c3"); + EXPECT_EQ (children2string (c2), ""); + EXPECT_EQ (children2string (c3), ""); + EXPECT_EQ (parents2string (c2), "c1"); + EXPECT_EQ (parents2string (c3), "c1"); + EXPECT_EQ (nl->top_circuit_count (), size_t (1)); + EXPECT_EQ (td2string (nl.get ()), "c1,c2,c3"); + EXPECT_EQ (bu2string (nl.get ()), "c3,c2,c1"); + + db::SubCircuit *sc4 = new db::SubCircuit (*sc2); + sc4->set_name ("sc4"); + c3->add_subcircuit (sc4); + + EXPECT_EQ (children2string (c1), "c2,c3"); + EXPECT_EQ (children2string (c2), ""); + EXPECT_EQ (children2string (c3), "c2"); + EXPECT_EQ (parents2string (c2), "c1,c3"); + EXPECT_EQ (parents2string (c3), "c1"); + EXPECT_EQ (nl->top_circuit_count (), size_t (1)); + EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2"); + EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1"); +} diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 04249daab..3c42392f3 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -563,6 +563,98 @@ class DBNetlist_TestClass < TestBase end + def test_10_NetlistTopology + + nl = RBA::Netlist::new + assert_equal(nl.top_circuit_count, 0) + + c1 = RBA::Circuit::new + c1.name = "C1" + nl.add(c1) + assert_equal(nl.top_circuit_count, 1) + + c2 = RBA::Circuit::new + c2.name = "C2" + nl.add(c2) + assert_equal(nl.top_circuit_count, 2) + + c3 = RBA::Circuit::new + c3.name = "C3" + nl.add(c3) + assert_equal(nl.top_circuit_count, 3) + + names = [] + nl.each_circuit_top_down { |c| names << c.name } + assert_equal(names.join(","), "C1,C2,C3") + + names = [] + nl.each_circuit_bottom_up { |c| names << c.name } + assert_equal(names.join(","), "C3,C2,C1") + + names = [] + c1.each_child { |c| names << c.name } + assert_equal(names.join(","), "") + + names = [] + c2.each_parent { |c| names << c.name } + assert_equal(names.join(","), "") + + c1.create_subcircuit(c2) + assert_equal(nl.top_circuit_count, 2) + + names = [] + c1.each_child { |c| names << c.name } + assert_equal(names.join(","), "C2") + + names = [] + c2.each_parent { |c| names << c.name } + assert_equal(names.join(","), "C1") + + c1.create_subcircuit(c2) + c1.create_subcircuit(c3) + assert_equal(nl.top_circuit_count, 1) + + names = [] + c1.each_child { |c| names << c.name } + assert_equal(names.join(","), "C2,C3") + + names = [] + c2.each_parent { |c| names << c.name } + assert_equal(names.join(","), "C1") + + names = [] + c3.each_parent { |c| names << c.name } + assert_equal(names.join(","), "C1") + + names = [] + nl.each_circuit_top_down { |c| names << c.name } + assert_equal(names.join(","), "C1,C2,C3") + + names = [] + nl.each_circuit_bottom_up { |c| names << c.name } + assert_equal(names.join(","), "C3,C2,C1") + + c3.create_subcircuit(c2) + assert_equal(nl.top_circuit_count, 1) + + names = [] + c2.each_parent { |c| names << c.name } + assert_equal(names.join(","), "C1,C3") + + names = [] + c3.each_parent { |c| names << c.name } + assert_equal(names.join(","), "C1") + + names = [] + nl.each_circuit_top_down { |c| names << c.name } + assert_equal(names.join(","), "C1,C3,C2") + + names = [] + nl.each_circuit_bottom_up { |c| names << c.name } + assert_equal(names.join(","), "C2,C3,C1") + + end + end load("test_epilogue.rb") From 54adb84e275b8005b92d31268f55dd35f4534b10 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Dec 2018 01:04:48 +0100 Subject: [PATCH 141/335] WIP: locking of netlist for better performance. --- src/db/db/dbNetlist.cc | 34 ++++++++++++++++---- src/db/db/dbNetlist.h | 50 +++++++++++++++++++++++++++++ src/db/unit_tests/dbNetlistTests.cc | 10 ++++++ 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 0e954a4e4..0b6631132 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -1129,13 +1129,13 @@ size_t DeviceClass::terminal_id_for_name (const std::string &name) const // Netlist class implementation Netlist::Netlist () - : m_valid_topology (false) + : m_valid_topology (false), m_lock_count (0) { m_circuits.changed ().add (this, &Netlist::invalidate_topology); } Netlist::Netlist (const Netlist &other) - : m_valid_topology (false) + : m_valid_topology (false), m_lock_count (0) { operator= (other); m_circuits.changed ().add (this, &Netlist::invalidate_topology); @@ -1173,11 +1173,16 @@ Netlist &Netlist::operator= (const Netlist &other) void Netlist::invalidate_topology () { if (m_valid_topology) { + m_valid_topology = false; - m_top_circuits = 0; - m_top_down_circuits.clear (); - m_child_circuits.clear (); - m_parent_circuits.clear (); + + if (m_lock_count == 0) { + m_top_circuits = 0; + m_top_down_circuits.clear (); + m_child_circuits.clear (); + m_parent_circuits.clear (); + } + } } @@ -1195,6 +1200,8 @@ void Netlist::validate_topology () { if (m_valid_topology) { return; + } else if (m_lock_count > 0) { + return; } m_child_circuits.clear (); @@ -1295,6 +1302,21 @@ void Netlist::validate_topology () m_valid_topology = true; } +void Netlist::lock () +{ + if (m_lock_count == 0) { + validate_topology (); + } + ++m_lock_count; +} + +void Netlist::unlock () +{ + if (m_lock_count > 0) { + --m_lock_count; + } +} + const tl::vector &Netlist::child_circuits (Circuit *circuit) { if (! m_valid_topology) { diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index cbdedddf8..cf1016e97 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -1806,6 +1806,30 @@ public: */ void clear (); + /** + * @brief Starts a sequence of operations during which topology updates are not desired + * + * If the hierarchy is modified, the topology information (top-down order, children + * and parent information) may be recomputed frequently. This may cause performance issues + * and may not be desired. + * + * Calling this method will bring the netlist into a state in which updates on the + * list will not happen. Using "unlock" will end this state. + * + * "lock" and "unlock" are incremental and can be nested. Use "NetlistLocker" for safe locking + * and unlocking. + * + * Before lock, the state will be validated, so inside the locked operation, the topology + * information will be valid with respect to the initial state. + */ + void lock (); + + /** + * @brief Ends a sequence of operations during which topology updates are not desired + * See "lock" for more details. + */ + void unlock (); + /** * @brief Adds a circuit to this netlist * @@ -1974,6 +1998,7 @@ private: circuit_list m_circuits; device_class_list m_device_classes; bool m_valid_topology; + int m_lock_count; tl::vector m_top_down_circuits; tl::vector > m_child_circuits; tl::vector > m_parent_circuits; @@ -1986,6 +2011,31 @@ private: const tl::vector &parent_circuits (Circuit *circuit); }; +/** + * @brief A helper class using RAII for safe locking/unlocking + */ +class DB_PUBLIC NetlistLocker +{ +public: + NetlistLocker (Netlist *netlist) + : mp_netlist (netlist) + { + if (mp_netlist.get ()) { + mp_netlist->lock (); + } + } + + ~NetlistLocker () + { + if (mp_netlist.get ()) { + mp_netlist->unlock (); + } + } + +private: + tl::weak_ptr mp_netlist; +}; + } #endif diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 3edfe1503..a25147743 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -902,9 +902,19 @@ TEST(12_NetlistTopology) EXPECT_EQ (td2string (nl.get ()), "c1,c2"); EXPECT_EQ (bu2string (nl.get ()), "c2,c1"); + std::auto_ptr locker (new db::NetlistLocker (nl.get ())); + db::Circuit *c3 = new db::Circuit (); c3->set_name ("c3"); nl->add_circuit (c3); + + // because we locked, it did not get updated: + EXPECT_EQ (nl->top_circuit_count (), size_t (2)); + EXPECT_EQ (td2string (nl.get ()), "c1,c2"); + EXPECT_EQ (bu2string (nl.get ()), "c2,c1"); + locker.reset (0); + + // after removing the lock, it's updated EXPECT_EQ (nl->top_circuit_count (), size_t (3)); EXPECT_EQ (td2string (nl.get ()), "c1,c2,c3"); EXPECT_EQ (bu2string (nl.get ()), "c3,c2,c1"); From edbafae43ef3e67cdbbacba8e4e23e5ff2a50886 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Dec 2018 20:50:35 +0100 Subject: [PATCH 142/335] WIP: make top level pins for named nets and netlist purging. Tests. --- src/db/db/dbNetlist.cc | 47 +++++++++++++++++++ src/db/db/dbNetlist.h | 17 +++++++ src/db/db/dbNetlistExtractor.cc | 12 ++--- src/db/unit_tests/dbNetlistExtractorTests.cc | 49 +++++++++++++++----- 4 files changed, 108 insertions(+), 17 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 0b6631132..c48ce4b40 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -1446,6 +1446,53 @@ void Netlist::purge_nets () } } +void Netlist::make_top_level_pins () +{ + size_t ntop = top_circuit_count (); + for (top_down_circuit_iterator c = begin_top_down (); c != end_top_down () && ntop > 0; ++c, --ntop) { + + Circuit *circuit = *c; + + if (circuit->pin_count () == 0) { + + // create pins for the named nets and connect them + for (Circuit::net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { + if (! n->name ().empty () && n->terminal_count () + n->pin_count () > 0) { + Pin pin (n->name ()); + pin = circuit->add_pin (pin); + circuit->connect_pin (pin.id (), n.operator-> ()); + } + } + + } + + } +} + +void Netlist::purge () +{ + // This locking is very important as we do not want to recompute the bottom-up list + // while iterating. + NetlistLocker locker (this); + + for (bottom_up_circuit_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) { + + Circuit *circuit = *c; + + circuit->purge_nets (); + if (circuit->begin_nets () == circuit->end_nets ()) { + + // No nets left: delete the subcircuits that refer to us and finally delete the circuit + while (circuit->begin_refs () != circuit->end_refs ()) { + delete circuit->begin_refs ().operator-> (); + } + delete circuit; + + } + + } +} + void Netlist::combine_devices () { for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index cf1016e97..da817218b 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -1984,6 +1984,23 @@ public: */ void purge_nets (); + /** + * @brief Creates pins for top-level circuits + * + * This method will turn all named nets of top-level circuits (such that are not + * referenced by subcircuits) into pins. This method can be used before purge to + * avoid that purge will remove nets which are directly connecting to subcircuits. + */ + void make_top_level_pins (); + + /** + * @brief Purge unused nets, circuits and subcircuits + * + * This method will purge all nets which return "floating". Circuits which don't have any + * nets (or only floating ones) and removed. Their subcircuits are disconnected. + */ + void purge (); + /** * @brief Combine devices * diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 05d492366..91650e852 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -98,12 +98,6 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect net->set_cluster_id (*c); circuit->add_net (net); - if (! clusters.is_root (*c)) { - // a non-root cluster makes a pin - size_t pin_id = make_pin (circuit, net); - c2p.insert (std::make_pair (*c, pin_id)); - } - // make subcircuit connections (also make the subcircuits if required) from the connections of the clusters make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster, layout.dbu ()); @@ -121,6 +115,12 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } } + if (! clusters.is_root (*c)) { + // a non-root cluster makes a pin + size_t pin_id = make_pin (circuit, net); + c2p.insert (std::make_pair (*c, pin_id)); + } + } } diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 14f6517c4..c148408a3 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -480,17 +480,17 @@ TEST(1_DeviceAndNetExtraction) // compare netlist as string EXPECT_EQ (netlist2string (nl), "Circuit RINGO ():\n" - " XINV2 $1 ($1=$I8,$2=FB,$3=OSC,$4=VSS,$5=VDD)\n" - " XINV2 $2 ($1=FB,$2=$I38,$3=$I19,$4=VSS,$5=VDD)\n" - " XINV2 $3 ($1=$I19,$2=$I39,$3=$I1,$4=VSS,$5=VDD)\n" - " XINV2 $4 ($1=$I1,$2=$I40,$3=$I2,$4=VSS,$5=VDD)\n" - " XINV2 $5 ($1=$I2,$2=$I41,$3=$I3,$4=VSS,$5=VDD)\n" - " XINV2 $6 ($1=$I3,$2=$I42,$3=$I4,$4=VSS,$5=VDD)\n" - " XINV2 $7 ($1=$I4,$2=$I43,$3=$I5,$4=VSS,$5=VDD)\n" - " XINV2 $8 ($1=$I5,$2=$I44,$3=$I6,$4=VSS,$5=VDD)\n" - " XINV2 $9 ($1=$I6,$2=$I45,$3=$I7,$4=VSS,$5=VDD)\n" - " XINV2 $10 ($1=$I7,$2=$I46,$3=$I8,$4=VSS,$5=VDD)\n" - "Circuit INV2 ($1=IN,$2=$2,$3=OUT,$4=$4,$5=$5):\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" @@ -502,6 +502,33 @@ TEST(1_DeviceAndNetExtraction) "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" ); + // doesn't do anything here, but we test that this does not destroy anything: + nl.combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + nl.make_top_level_pins (); + nl.purge (); + + // compare netlist as string + EXPECT_EQ (netlist2string (nl), + "Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 (IN=$I19,$2=(null),OUT=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 (IN=$I1,$2=(null),OUT=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 (IN=$I2,$2=(null),OUT=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 (IN=$I3,$2=(null),OUT=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 (IN=$I4,$2=(null),OUT=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 (IN=$I5,$2=(null),OUT=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + ); + // compare the collected test data std::string au = tl::testsrc (); From 45b35f3aae0cd92b41fad552178d42496007ee45 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Dec 2018 22:18:58 +0100 Subject: [PATCH 143/335] WIP: to_string for netlist, tests, some bugfixes on device combination. --- src/db/db/dbNetlist.cc | 144 ++++- src/db/db/dbNetlist.h | 16 +- src/db/db/dbNetlistDeviceClasses.cc | 5 + src/db/db/dbNetlistDeviceClasses.h | 5 + src/db/db/gsiDeclDbNetlist.cc | 21 + .../unit_tests/dbNetlistDeviceClassesTests.cc | 555 ++++++++++++++++++ src/db/unit_tests/dbNetlistExtractorTests.cc | 97 +-- src/db/unit_tests/unit_tests.pro | 3 +- testdata/ruby/dbNetlist.rb | 5 + 9 files changed, 746 insertions(+), 105 deletions(-) create mode 100644 src/db/unit_tests/dbNetlistDeviceClassesTests.cc diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index c48ce4b40..3c378a4b0 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -380,6 +380,12 @@ Net::Net () // .. nothing yet .. } +Net::Net (const std::string &name) + : m_cluster_id (0), mp_circuit (0) +{ + m_name = name; +} + Net::Net (const Net &other) : m_cluster_id (0), mp_circuit (0) { @@ -861,7 +867,7 @@ void Circuit::purge_nets () */ static void check_device_before_remove (db::Circuit *c, const db::Device *d) { - if (d->device_class () != 0) { + if (d->device_class () == 0) { throw tl::Exception (tl::to_string (tr ("Internal error: No device class after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name ()); } const std::vector &pd = d->device_class ()->terminal_definitions (); @@ -872,11 +878,13 @@ static void check_device_before_remove (db::Circuit *c, const db::Device *d) } } -void Circuit::combine_parallel_devices (const db::DeviceClass &cls) +bool Circuit::combine_parallel_devices (const db::DeviceClass &cls) { typedef std::vector key_type; std::map > combination_candidates; + bool any = false; + // identify the candidates for combination - all devices sharing the same nets // are candidates for combination in parallel mode for (device_iterator d = begin_devices (); d != end_devices (); ++d) { @@ -904,12 +912,13 @@ void Circuit::combine_parallel_devices (const db::DeviceClass &cls) for (std::map >::iterator cc = combination_candidates.begin (); cc != combination_candidates.end (); ++cc) { std::vector &cl = cc->second; - for (size_t i = 0; i != cl.size () - 1; ++i) { - for (size_t j = i + 1; j != cl.size (); ) { + for (size_t i = 0; i < cl.size () - 1; ++i) { + for (size_t j = i + 1; j < cl.size (); ) { if (cls.combine_devices (cl [i], cl [j])) { check_device_before_remove (this, cl [j]); // sanity check delete cl [j]; cl.erase (cl.begin () + j); + any = true; } else { ++j; } @@ -917,6 +926,8 @@ void Circuit::combine_parallel_devices (const db::DeviceClass &cls) } } + + return any; } static std::pair attached_two_devices (db::Net &net, const db::DeviceClass &cls) @@ -955,8 +966,10 @@ static bool same_or_swapped (const std::pair &p1, const std::pair &p return (p1.first == p2.first && p1.second == p2.second) || (p1.first == p2.second && p1.second == p2.first); } -void Circuit::combine_serial_devices (const db::DeviceClass &cls) +bool Circuit::combine_serial_devices(const db::DeviceClass &cls) { + bool any = false; + for (net_iterator n = begin_nets (); n != end_nets (); ++n) { std::pair dd = attached_two_devices (*n, cls); @@ -993,11 +1006,14 @@ void Circuit::combine_serial_devices (const db::DeviceClass &cls) if (cls.combine_devices (dd.first, dd.second)) { check_device_before_remove (this, dd.second); // sanity check delete dd.second; + any = true; } } } + + return any; } void Circuit::combine_devices () @@ -1005,12 +1021,27 @@ void Circuit::combine_devices () tl_assert (netlist () != 0); for (Netlist::device_class_iterator dc = netlist ()->begin_device_classes (); dc != netlist ()->end_device_classes (); ++dc) { - if (dc->supports_parallel_combination ()) { - combine_parallel_devices (*dc); - } - if (dc->supports_serial_combination ()) { - combine_serial_devices (*dc); + + // repeat the combination step unless no combination happens - this is required to take care of combinations that arise after + // other combinations have been realized. + bool any = true; + while (any) { + + any = false; + + if (dc->supports_parallel_combination ()) { + if (combine_parallel_devices (*dc)) { + any = true; + } + } + if (dc->supports_serial_combination ()) { + if (combine_serial_devices (*dc)) { + any = true; + } + } + } + } } @@ -1500,4 +1531,97 @@ void Netlist::combine_devices () } } +static std::string net2string (const db::Net *net) +{ + return net ? tl::to_word_or_quoted_string (net->expanded_name ()) : "(null)"; +} + +static std::string device2string (const db::Device &device) +{ + if (device.name ().empty ()) { + return "$" + tl::to_string (device.id ()); + } else { + return tl::to_word_or_quoted_string (device.name ()); + } +} + +static std::string subcircuit2string (const db::SubCircuit &subcircuit) +{ + if (subcircuit.name ().empty ()) { + return "$" + tl::to_string (subcircuit.id ()); + } else { + return tl::to_word_or_quoted_string (subcircuit.name ()); + } +} + +static std::string pin2string (const db::Pin &pin) +{ + if (pin.name ().empty ()) { + // the pin ID is zero-based and essentially the index, so we add 1 to make it compliant with the other IDs + return "$" + tl::to_string (pin.id () + 1); + } else { + return tl::to_word_or_quoted_string (pin.name ()); + } +} + +std::string Netlist::to_string () const +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + + std::string ps; + for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) { + if (! ps.empty ()) { + ps += ","; + } + ps += pin2string (*p) + "=" + net2string (c->net_for_pin (p->id ())); + } + + res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n"; + +#if 0 // for debugging + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n"; + } +#endif + + for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + std::string ts; + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + if (t != td.begin ()) { + ts += ","; + } + ts += t->name () + "=" + net2string (d->net_for_terminal (t->id ())); + } + std::string ps; + const std::vector &pd = d->device_class ()->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (p != pd.begin ()) { + ps += ","; + } + ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ())); + } + res += std::string (" D") + d->device_class ()->name () + " " + device2string (*d) + " (" + ts + ") [" + ps + "]\n"; + } + + for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { + std::string ps; + const db::SubCircuit &subcircuit = *sc; + const db::Circuit *circuit = sc->circuit_ref (); + for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) { + if (p != circuit->begin_pins ()) { + ps += ","; + } + const db::Pin &pin = *p; + ps += pin2string (pin) + "=" + net2string (subcircuit.net_for_pin (pin.id ())); + } + res += std::string (" X") + circuit->name () + " " + subcircuit2string (*sc) + " (" + ps + ")\n"; + } + + } + + return res; +} + } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index da817218b..6079bc9a7 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -301,6 +301,11 @@ public: */ Net (); + /** + * @brief Creates a empty net with the give name + */ + Net (const std::string &name); + /** * @brief Copy constructor */ @@ -1374,8 +1379,8 @@ private: void translate_circuits (const std::map &map); void translate_device_classes (const std::map &map); void set_netlist (Netlist *netlist); - void combine_parallel_devices (const db::DeviceClass &cls); - void combine_serial_devices (const db::DeviceClass &cls); + bool combine_parallel_devices (const db::DeviceClass &cls); + bool combine_serial_devices (const db::DeviceClass &cls); void validate_device_id_table (); void invalidate_device_id_table (); @@ -1806,6 +1811,13 @@ public: */ void clear (); + /** + * @brief Returns a string representation of the netlist + * + * This method is basically intended to testing. + */ + std::string to_string () const; + /** * @brief Starts a sequence of operations during which topology updates are not desired * diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index f97cca187..29213cd8a 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -68,6 +68,11 @@ bool DeviceClassTwoTerminalDevice::combine_devices (Device *a, Device *b) const // ------------------------------------------------------------------------------------ // DeviceClassResistor implementation +DB_PUBLIC size_t DeviceClassResistor::param_id_R = 0; + +DB_PUBLIC size_t DeviceClassResistor::terminal_id_A = 0; +DB_PUBLIC size_t DeviceClassResistor::terminal_id_B = 1; + DeviceClassResistor::DeviceClassResistor () { add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index 949c6b6b0..b40b1a985 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -60,6 +60,11 @@ public: return new DeviceClassResistor (*this); } + static size_t param_id_R; + + static size_t terminal_id_A; + static size_t terminal_id_B; + virtual void parallel (Device *a, Device *b) const; virtual void serial (Device *a, Device *b) const; }; diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index ea9b8c71c..51e6180dc 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -105,6 +105,8 @@ Class decl_dbDevice ("db", "Device", "Devices connect to nets through the \\Device#connect_terminal method. " "Device terminals can be disconnected using \\Device#disconnect_terminal.\n" "\n" + "Device objects are created inside a circuit with \\Circuit#create_device.\n" + "\n" "This class has been added in version 0.26." ); @@ -177,6 +179,8 @@ Class decl_dbSubCircuit ("db", "SubCircuit", "Subcircuits connect to nets through the \\SubCircuit#connect_pin method. " "SubCircuit pins can be disconnected using \\SubCircuit#disconnect_pin.\n" "\n" + "Subcircuit objects are created inside a circuit with \\Circuit#create_subcircuit.\n" + "\n" "This class has been added in version 0.26." ); @@ -286,6 +290,8 @@ Class decl_dbNet ("db", "Net", "pin or subcircuits of outgoing pins of the circuit the net lives in. " "Terminals are connections made to specific terminals of devices.\n" "\n" + "Net objects are created inside a circuit with \\Circuit#create_net.\n" + "\n" "To connect a net to an outgoing pin of a circuit, use \\Circuit#connect_pin, to " "disconnect a net from an outgoing pin use \\Circuit#disconnect_pin. " "To connect a net to a pin of a subcircuit, use \\SubCircuit#connect_pin, to " @@ -840,6 +846,10 @@ Class decl_dbNetlist ("db", "Netlist", gsi::iterator ("each_device_class", (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::begin_device_classes, (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::end_device_classes, "@brief Iterates over the device classes of the netlist" ) + + gsi::method ("to_s", &db::Netlist::to_string, + "@brief Converts the netlist to a string representation.\n" + "This method is intended for test purposes mainly." + ) + gsi::method ("combine_devices", &db::Netlist::combine_devices, "@brief Combines devices where possible\n" "This method will combine devices that can be combined according " @@ -847,6 +857,17 @@ Class decl_dbNetlist ("db", "Netlist", "For example, serial or parallel resistors can be combined into " "a single resistor.\n" ) + + gsi::method ("make_top_level_pins", &db::Netlist::make_top_level_pins, + "@brief Creates pins for top-level circuits.\n" + "This method will turn all named nets of top-level circuits (such that are not " + "referenced by subcircuits) into pins. This method can be used before purge to " + "avoid that purge will remove nets which are directly connecting to subcircuits." + ) + + gsi::method ("purge", &db::Netlist::purge, + "@brief Purge unused nets, circuits and subcircuits.\n" + "This method will purge all nets which return \\floating == true. Circuits which don't have any " + "nets (or only floating ones) and removed. Their subcircuits are disconnected." + ) + gsi::method ("purge_nets", &db::Netlist::purge_nets, "@brief Purges floating nets.\n" "Floating nets can be created as effect of reconnections of devices or pins. " diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc new file mode 100644 index 000000000..9670944b8 --- /dev/null +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -0,0 +1,555 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlistDeviceClasses.h" +#include "dbNetlist.h" +#include "tlUnitTest.h" + +#include +#include + +TEST(1_SerialResistors) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n3) [R=4]\n" + ); +}; + +TEST(1_SerialResistors1Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n3,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n3) [R=4]\n" + ); +}; + +TEST(1_SerialResistors1OtherSwapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n2,B=n1) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n3,B=n1) [R=4]\n" + ); +}; + +TEST(1_SerialResistors2Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n2,B=n1) [R=1]\n" + " D r2 (A=n3,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n3,B=n1) [R=4]\n" + ); +}; + +TEST(1_SerialResistorsNoCombination) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_c.id (), n2); // prevents combination + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3,C=n2):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3,C=n2):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); +}; + +TEST(1_ParallelResistors) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=2]\n" + " D r2 (A=n1,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=1.2]\n" + ); +}; + +TEST(1_ParallelResistors1Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=2]\n" + " D r2 (A=n1,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=1.2]\n" + ); +}; + +TEST(1_ParallelResistors1OtherSwapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=2]\n" + " D r2 (A=n2,B=n1) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=1.2]\n" + ); +}; + +TEST(1_ParallelResistors2Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=2]\n" + " D r2 (A=n2,B=n1) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=1.2]\n" + ); +}; + +TEST(1_ComplexRegistorCombination) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + /** + * (n2) + * +--[ r1=1.0 ]--+--[ r2=1.0 ]--+ + * | | + * --x (n1) (n3) x--[ r4=0.8 ]--+-- + * | | (n4) + * +----------[ r3=3.0 ]---------+ + */ + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r3 = new db::Device (res, "r3"); + r3->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + db::Device *r4 = new db::Device (res, "r4"); + r4->set_parameter_value (db::DeviceClassResistor::param_id_R, 0.8); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + circuit->add_device (r3); + circuit->add_device (r4); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + r3->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + r3->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + r4->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); + + db::Net *n4 = new db::Net ("n4"); + circuit->add_net (n4); + circuit->connect_pin (pin_b.id (), n4); + r4->connect_terminal (db::DeviceClassResistor::terminal_id_B, n4); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n4):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=1]\n" + " D r3 (A=n1,B=n3) [R=3]\n" + " D r4 (A=n3,B=n4) [R=0.8]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n4):\n" + " D r4 (A=n1,B=n4) [R=2]\n" + ); +}; + diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index c148408a3..52dd66406 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -49,11 +49,6 @@ static unsigned int layer_of (const db::Region ®ion) return db::DeepLayer (region).layer (); } -static std::string net_name (const db::Net *net) -{ - return net ? net->expanded_name () : "(null)"; -} - static std::string device_name (const db::Device &device) { if (device.name ().empty ()) { @@ -63,26 +58,6 @@ static std::string device_name (const db::Device &device) } } -static std::string subcircuit_name (const db::SubCircuit &subcircuit) -{ - if (subcircuit.name ().empty ()) { - return "$" + tl::to_string (subcircuit.id ()); - } else { - return subcircuit.name (); - } -} - -static std::string pin_name (const db::Pin &pin) -{ - if (pin.name ().empty ()) { - // the pin ID is zero-based and essentially the index, so we add 1 to make it compliant with the other IDs - return "$" + tl::to_string (pin.id () + 1); - } else { - return pin.name (); - } -} - - class MOSFETExtractor : public db::NetlistDeviceExtractor { @@ -229,9 +204,7 @@ static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_la return lid; } -// @@@ TODO: move this somewhere else - -static void dump_nets (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) { for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { @@ -248,7 +221,7 @@ static void dump_nets (const db::Netlist &nl, const db::hier_clustersname () + "_" + net_name (n.operator-> ()); + std::string nn = "NET_" + c->name () + "_" + n->expanded_name (); db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ())); cell.insert (db::CellInstArray (db::CellInst (net_cell.cell_index ()), db::Trans ())); @@ -266,66 +239,6 @@ static void dump_nets (const db::Netlist &nl, const db::hier_clustersbegin_pins (); p != c->end_pins (); ++p) { - if (! ps.empty ()) { - ps += ","; - } - ps += pin_name (*p) + "=" + net_name (c->net_for_pin (p->id ())); - } - - res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n"; - -#if 0 // for debugging - for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n"; - } -#endif - - for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { - std::string ts; - const std::vector &td = d->device_class ()->terminal_definitions (); - for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { - if (t != td.begin ()) { - ts += ","; - } - ts += t->name () + "=" + net_name (d->net_for_terminal (t->id ())); - } - std::string ps; - const std::vector &pd = d->device_class ()->parameter_definitions (); - for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { - if (p != pd.begin ()) { - ps += ","; - } - ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ())); - } - res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d) + " (" + ts + ") [" + ps + "]\n"; - } - - for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { - std::string ps; - const db::SubCircuit &subcircuit = *sc; - const db::Circuit *circuit = sc->circuit_ref (); - for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) { - if (p != circuit->begin_pins ()) { - ps += ","; - } - const db::Pin &pin = *p; - ps += pin_name (pin) + "=" + net_name (subcircuit.net_for_pin (pin.id ())); - } - res += std::string (" X") + circuit->name () + " " + subcircuit_name (*sc) + " (" + ps + ")\n"; - } - - } - - return res; -} - TEST(1_DeviceAndNetExtraction) { db::Layout ly; @@ -475,10 +388,10 @@ TEST(1_DeviceAndNetExtraction) // write nets to layout db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); - dump_nets (nl, net_ex.clusters (), ly, dump_map, cm); + dump_nets_to_layout (nl, net_ex.clusters (), ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (netlist2string (nl), + EXPECT_EQ (nl.to_string (), "Circuit RINGO ():\n" " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" " XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)\n" @@ -510,7 +423,7 @@ TEST(1_DeviceAndNetExtraction) nl.purge (); // compare netlist as string - EXPECT_EQ (netlist2string (nl), + EXPECT_EQ (nl.to_string (), "Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n" " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" " XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD)\n" diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index a6f455bac..693acf642 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -62,7 +62,8 @@ SOURCES = \ dbNetlistPropertyTests.cc \ dbNetlistTests.cc \ dbNetlistExtractorTests.cc \ - dbNetlistDeviceExtractorTests.cc + dbNetlistDeviceExtractorTests.cc \ + dbNetlistDeviceClassesTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 3c42392f3..9f94ff9ad 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -561,6 +561,11 @@ class DBNetlist_TestClass < TestBase assert_equal(d1.parameter(0), -0.5) assert_equal(d1.parameter(1), 42) + assert_equal(nl.to_s, < Date: Sat, 29 Dec 2018 22:58:29 +0100 Subject: [PATCH 144/335] WIP: tests for specialized device classes and device combination --- src/db/db/dbNetlistDeviceClasses.cc | 36 +- src/db/db/dbNetlistDeviceClasses.h | 34 + .../unit_tests/dbNetlistDeviceClassesTests.cc | 983 +++++++++++++++++- src/db/unit_tests/dbNetlistExtractorTests.cc | 10 +- 4 files changed, 1048 insertions(+), 15 deletions(-) diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index 29213cd8a..11f125a71 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -98,6 +98,11 @@ void DeviceClassResistor::serial (Device *a, Device *b) const // ------------------------------------------------------------------------------------ // DeviceClassCapacitor implementation +DB_PUBLIC size_t DeviceClassCapacitor::param_id_C = 0; + +DB_PUBLIC size_t DeviceClassCapacitor::terminal_id_A = 0; +DB_PUBLIC size_t DeviceClassCapacitor::terminal_id_B = 1; + DeviceClassCapacitor::DeviceClassCapacitor () { add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); @@ -123,6 +128,11 @@ void DeviceClassCapacitor::parallel (Device *a, Device *b) const // ------------------------------------------------------------------------------------ // DeviceClassInductor implementation +DB_PUBLIC size_t DeviceClassInductor::param_id_L = 0; + +DB_PUBLIC size_t DeviceClassInductor::terminal_id_A = 0; +DB_PUBLIC size_t DeviceClassInductor::terminal_id_B = 1; + DeviceClassInductor::DeviceClassInductor () { add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); @@ -148,6 +158,11 @@ void DeviceClassInductor::serial (Device *a, Device *b) const // ------------------------------------------------------------------------------------ // DeviceClassInductor implementation +DB_PUBLIC size_t DeviceClassDiode::param_id_A = 0; + +DB_PUBLIC size_t DeviceClassDiode::terminal_id_A = 0; +DB_PUBLIC size_t DeviceClassDiode::terminal_id_C = 1; + DeviceClassDiode::DeviceClassDiode () { add_terminal_definition (db::DeviceTerminalDefinition ("A", "Anode")); @@ -164,7 +179,7 @@ bool DeviceClassDiode::combine_devices (Device *a, Device *b) const const db::Net *nb2 = b->net_for_terminal (1); // only parallel diodes can be combined and their areas will add - if ((na1 == nb1 && na2 == nb2) || (na1 == nb2 && na2 == nb1)) { + if (na1 == nb1 && na2 == nb2) { a->set_parameter_value (0, a->parameter_value (0) + b->parameter_value (0)); b->connect_terminal (0, 0); @@ -180,6 +195,15 @@ bool DeviceClassDiode::combine_devices (Device *a, Device *b) const // ------------------------------------------------------------------------------------ // DeviceClassMOS3Transistor implementation +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_L = 0; +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_W = 1; +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_AS = 2; +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_AD = 3; + +DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_S = 0; +DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_G = 1; +DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_D = 2; + DeviceClassMOS3Transistor::DeviceClassMOS3Transistor () { add_terminal_definition (db::DeviceTerminalDefinition ("S", "Source")); @@ -226,6 +250,16 @@ bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const // ------------------------------------------------------------------------------------ // DeviceClassMOS4Transistor implementation +DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_L = 0; +DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_W = 1; +DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_AS = 2; +DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_AD = 3; + +DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_S = 0; +DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_G = 1; +DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_D = 2; +DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_B = 3; + DeviceClassMOS4Transistor::DeviceClassMOS4Transistor () { add_terminal_definition (db::DeviceTerminalDefinition ("S", "Source")); diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index b40b1a985..dccc580d9 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -85,6 +85,11 @@ public: return new DeviceClassCapacitor (*this); } + static size_t param_id_C; + + static size_t terminal_id_A; + static size_t terminal_id_B; + virtual void parallel (Device *a, Device *b) const; virtual void serial (Device *a, Device *b) const; }; @@ -105,6 +110,11 @@ public: return new DeviceClassInductor (*this); } + static size_t param_id_L; + + static size_t terminal_id_A; + static size_t terminal_id_B; + virtual void parallel (Device *a, Device *b) const; virtual void serial (Device *a, Device *b) const; }; @@ -121,6 +131,11 @@ class DB_PUBLIC DeviceClassDiode public: DeviceClassDiode (); + static size_t param_id_A; + + static size_t terminal_id_A; + static size_t terminal_id_C; + virtual db::DeviceClass *clone () const { return new DeviceClassDiode (*this); @@ -142,6 +157,15 @@ class DB_PUBLIC DeviceClassMOS3Transistor public: DeviceClassMOS3Transistor (); + static size_t param_id_L; + static size_t param_id_W; + static size_t param_id_AS; + static size_t param_id_AD; + + static size_t terminal_id_S; + static size_t terminal_id_G; + static size_t terminal_id_D; + virtual db::DeviceClass *clone () const { return new DeviceClassMOS3Transistor (*this); @@ -162,6 +186,16 @@ class DB_PUBLIC DeviceClassMOS4Transistor public: DeviceClassMOS4Transistor (); + static size_t param_id_L; + static size_t param_id_W; + static size_t param_id_AS; + static size_t param_id_AD; + + static size_t terminal_id_S; + static size_t terminal_id_G; + static size_t terminal_id_D; + static size_t terminal_id_B; + virtual db::DeviceClass *clone () const { return new DeviceClassMOS4Transistor (*this); diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc index 9670944b8..2864e8cfe 100644 --- a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -79,7 +79,7 @@ TEST(1_SerialResistors) ); }; -TEST(1_SerialResistors1Swapped) +TEST(2_SerialResistors1Swapped) { db::DeviceClassResistor *res = new db::DeviceClassResistor (); @@ -130,7 +130,7 @@ TEST(1_SerialResistors1Swapped) ); }; -TEST(1_SerialResistors1OtherSwapped) +TEST(3_SerialResistors1OtherSwapped) { db::DeviceClassResistor *res = new db::DeviceClassResistor (); @@ -181,7 +181,7 @@ TEST(1_SerialResistors1OtherSwapped) ); }; -TEST(1_SerialResistors2Swapped) +TEST(4_SerialResistors2Swapped) { db::DeviceClassResistor *res = new db::DeviceClassResistor (); @@ -232,7 +232,7 @@ TEST(1_SerialResistors2Swapped) ); }; -TEST(1_SerialResistorsNoCombination) +TEST(5_SerialResistorsNoCombination) { db::DeviceClassResistor *res = new db::DeviceClassResistor (); @@ -286,7 +286,7 @@ TEST(1_SerialResistorsNoCombination) ); }; -TEST(1_ParallelResistors) +TEST(6_ParallelResistors) { db::DeviceClassResistor *res = new db::DeviceClassResistor (); @@ -334,7 +334,7 @@ TEST(1_ParallelResistors) ); }; -TEST(1_ParallelResistors1Swapped) +TEST(7_ParallelResistors1Swapped) { db::DeviceClassResistor *res = new db::DeviceClassResistor (); @@ -382,7 +382,7 @@ TEST(1_ParallelResistors1Swapped) ); }; -TEST(1_ParallelResistors1OtherSwapped) +TEST(8_ParallelResistors1OtherSwapped) { db::DeviceClassResistor *res = new db::DeviceClassResistor (); @@ -430,7 +430,7 @@ TEST(1_ParallelResistors1OtherSwapped) ); }; -TEST(1_ParallelResistors2Swapped) +TEST(9_ParallelResistors2Swapped) { db::DeviceClassResistor *res = new db::DeviceClassResistor (); @@ -478,7 +478,7 @@ TEST(1_ParallelResistors2Swapped) ); }; -TEST(1_ComplexRegistorCombination) +TEST(10_ComplexRegistorCombination) { db::DeviceClassResistor *res = new db::DeviceClassResistor (); @@ -553,3 +553,968 @@ TEST(1_ComplexRegistorCombination) ); }; +TEST(11_SerialInductors) +{ + db::DeviceClassInductor *ind = new db::DeviceClassInductor (); + + db::Netlist nl; + nl.add_device_class (ind); + + db::Device *l1 = new db::Device (ind, "l1"); + l1->set_parameter_value (db::DeviceClassInductor::param_id_L, 1.0); + db::Device *l2 = new db::Device (ind, "l2"); + l2->set_parameter_value (db::DeviceClassInductor::param_id_L, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (l1); + circuit->add_device (l2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + l1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + l1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + l2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + l2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D l1 (A=n1,B=n2) [L=1]\n" + " D l2 (A=n2,B=n3) [L=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D l1 (A=n1,B=n3) [L=4]\n" + ); +}; + +TEST(12_ParallelInductors) +{ + db::DeviceClassInductor *ind = new db::DeviceClassInductor (); + + db::Netlist nl; + nl.add_device_class (ind); + + db::Device *l1 = new db::Device (ind, "l1"); + l1->set_parameter_value (db::DeviceClassInductor::param_id_L, 2.0); + db::Device *l2 = new db::Device (ind, "l2"); + l2->set_parameter_value (db::DeviceClassInductor::param_id_L, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (l1); + circuit->add_device (l2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + l1->connect_terminal (db::DeviceClassInductor::terminal_id_A, n1); + l2->connect_terminal (db::DeviceClassInductor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + l1->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2); + l2->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D l1 (A=n1,B=n2) [L=2]\n" + " D l2 (A=n1,B=n2) [L=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D l1 (A=n1,B=n2) [L=1.2]\n" + ); +}; + +TEST(13_SerialCapacitors) +{ + db::DeviceClassCapacitor *cap = new db::DeviceClassCapacitor (); + + db::Netlist nl; + nl.add_device_class (cap); + + db::Device *c1 = new db::Device (cap, "c1"); + c1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 2.0); + db::Device *c2 = new db::Device (cap, "c2"); + c2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (c1); + circuit->add_device (c2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); + c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D c1 (A=n1,B=n2) [C=2]\n" + " D c2 (A=n2,B=n3) [C=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D c1 (A=n1,B=n3) [C=1.2]\n" + ); +}; + +TEST(14_ParallelCapacitors) +{ + db::DeviceClassCapacitor *cap = new db::DeviceClassCapacitor (); + + db::Netlist nl; + nl.add_device_class (cap); + + db::Device *c1 = new db::Device (cap, "c1"); + c1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 1.0); + db::Device *c2 = new db::Device (cap, "c2"); + c2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (c1); + circuit->add_device (c2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n1); + c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); + c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D c1 (A=n1,B=n2) [C=1]\n" + " D c2 (A=n1,B=n2) [C=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D c1 (A=n1,B=n2) [C=4]\n" + ); +}; + +TEST(15_SerialDiodes) +{ + db::DeviceClassDiode *diode = new db::DeviceClassDiode (); + + db::Netlist nl; + nl.add_device_class (diode); + + db::Device *d1 = new db::Device (diode, "d1"); + d1->set_parameter_value (db::DeviceClassDiode::param_id_A, 2.0); + db::Device *d2 = new db::Device (diode, "d2"); + d2->set_parameter_value (db::DeviceClassDiode::param_id_A, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_C, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D d1 (A=n1,C=n2) [A=2]\n" + " D d2 (A=n2,C=n3) [A=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // serial diodes are not combined! + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D d1 (A=n1,C=n2) [A=2]\n" + " D d2 (A=n2,C=n3) [A=3]\n" + ); +}; + +TEST(16_ParallelDiodes) +{ + db::DeviceClassDiode *diode = new db::DeviceClassDiode (); + + db::Netlist nl; + nl.add_device_class (diode); + + db::Device *d1 = new db::Device (diode, "d1"); + d1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.0); + db::Device *d2 = new db::Device (diode, "d2"); + d2->set_parameter_value (db::DeviceClassDiode::param_id_A, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_A, n1); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D d1 (A=n1,C=n2) [A=1]\n" + " D d2 (A=n1,C=n2) [A=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D d1 (A=n1,C=n2) [A=4]\n" + ); +}; + +TEST(17_AntiParallelDiodes) +{ + db::DeviceClassDiode *diode = new db::DeviceClassDiode (); + + db::Netlist nl; + nl.add_device_class (diode); + + db::Device *d1 = new db::Device (diode, "d1"); + d1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.0); + db::Device *d2 = new db::Device (diode, "d2"); + d2->set_parameter_value (db::DeviceClassDiode::param_id_A, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_A, n1); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_C, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_A, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D d1 (A=n1,C=n2) [A=1]\n" + " D d2 (A=n2,C=n1) [A=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // anti-parallel diodes are not combined + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D d1 (A=n1,C=n2) [A=1]\n" + " D d2 (A=n2,C=n1) [A=3]\n" + ); +}; + +TEST(20_ParallelMOS3Transistors) +{ + db::DeviceClassMOS3Transistor *cls = new db::DeviceClassMOS3Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n3,D=n2) [L=0.5,W=2,AS=3,AD=4]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7]\n" + ); +}; + +TEST(21_AntiParallelMOS3Transistors) +{ + db::DeviceClassMOS3Transistor *cls = new db::DeviceClassMOS3Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n2,G=n3,D=n1) [L=0.5,W=2,AS=3,AD=4]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7]\n" + ); +}; + +TEST(22_ParallelMOS3TransistorsDisconnectedGates) +{ + db::DeviceClassMOS3Transistor *cls = new db::DeviceClassMOS3Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c1 = circuit->add_pin (db::Pin ("C1")); + db::Pin pin_c2 = circuit->add_pin (db::Pin ("C2")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c1.id (), n3); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + + db::Net *n4 = new db::Net ("n4"); + circuit->add_net (n4); + circuit->connect_pin (pin_c2.id (), n4); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n4); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C1=n3,C2=n4):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // because of the disconnected gates, devices will no be joined: + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C1=n3,C2=n4):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4]\n" + ); +}; + +TEST(23_ParallelMOS3TransistorsDifferentLength) +{ + db::DeviceClassMOS3Transistor *cls = new db::DeviceClassMOS3Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.75); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // because of different length, the devices will not be combined: + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4]\n" + ); +}; + +TEST(30_ParallelMOS4Transistors) +{ + db::DeviceClassMOS4Transistor *cls = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + db::Pin pin_d = circuit->add_pin (db::Pin ("D")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + + db::Net *n0 = new db::Net ("n0"); + circuit->add_net (n0); + circuit->connect_pin (pin_d.id (), n0); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7]\n" + ); +}; + +TEST(31_AntiParallelMOS4Transistors) +{ + db::DeviceClassMOS4Transistor *cls = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + db::Pin pin_d = circuit->add_pin (db::Pin ("D")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + + db::Net *n0 = new db::Net ("n0"); + circuit->add_net (n0); + circuit->connect_pin (pin_d.id (), n0); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n2,G=n3,D=n1,B=n0) [L=0.5,W=2,AS=3,AD=4]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7]\n" + ); +}; + +TEST(32_ParallelMOS4TransistorsDisconnectedGates) +{ + db::DeviceClassMOS4Transistor *cls = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c1 = circuit->add_pin (db::Pin ("C1")); + db::Pin pin_c2 = circuit->add_pin (db::Pin ("C2")); + db::Pin pin_d = circuit->add_pin (db::Pin ("D")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + + db::Net *n3a = new db::Net ("n3a"); + circuit->add_net (n3a); + circuit->connect_pin (pin_c1.id (), n3a); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3a); + + db::Net *n3b = new db::Net ("n3b"); + circuit->add_net (n3b); + circuit->connect_pin (pin_c2.id (), n3b); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3b); + + db::Net *n0 = new db::Net ("n0"); + circuit->add_net (n0); + circuit->connect_pin (pin_d.id (), n0); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C1=n3a,C2=n3b,D=n0):\n" + " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // not combined because gate is different: + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C1=n3a,C2=n3b,D=n0):\n" + " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4]\n" + ); +}; + +TEST(33_ParallelMOS4TransistorsDisconnectedBulk) +{ + db::DeviceClassMOS4Transistor *cls = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + db::Pin pin_d1 = circuit->add_pin (db::Pin ("D1")); + db::Pin pin_d2 = circuit->add_pin (db::Pin ("D2")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + + db::Net *n0a = new db::Net ("n0a"); + circuit->add_net (n0a); + circuit->connect_pin (pin_d1.id (), n0a); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0a); + + db::Net *n0b = new db::Net ("n0b"); + circuit->add_net (n0b); + circuit->connect_pin (pin_d2.id (), n0b); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0b); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D1=n0a,D2=n0b):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4]\n" + ); + + // not combined because bulk is different: + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D1=n0a,D2=n0b):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4]\n" + ); +}; + +TEST(34_ParallelMOS4TransistorsDifferentLength) +{ + db::DeviceClassMOS4Transistor *cls = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.75); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + db::Pin pin_d = circuit->add_pin (db::Pin ("D")); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + + db::Net *n0 = new db::Net ("n0"); + circuit->add_net (n0); + circuit->connect_pin (pin_d.id (), n0); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // not combined because length is different: + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4]\n" + ); +}; + diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 52dd66406..3ee586167 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -133,8 +133,8 @@ public: db::Device *device = create_device (); - device->set_parameter_value ("W", dbu () * edges.length () * 0.5); - device->set_parameter_value ("L", dbu () * (p->perimeter () - edges.length ()) * 0.5); + device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, dbu () * edges.length () * 0.5); + device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, dbu () * (p->perimeter () - edges.length ()) * 0.5); int diff_index = 0; for (db::Region::const_iterator d = rdiff2gate.begin (); !d.at_end () && diff_index < 2; ++d, ++diff_index) { @@ -144,13 +144,13 @@ public: int n = rgates.selected_interacting (db::Region (*d)).size (); tl_assert (n > 0); - device->set_parameter_value (diff_index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n)); + device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, dbu () * dbu () * d->area () / double (n)); - define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_geometry_index, *d); + define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, terminal_geometry_index, *d); } - define_terminal (device, device->device_class ()->terminal_id_for_name ("G"), gate_geometry_index, *p); + define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_geometry_index, *p); // output the device for debugging device_out (device, rdiff2gate, rgate); From c571535e556999b94d0faddc890683553873f746 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Dec 2018 23:48:31 +0100 Subject: [PATCH 145/335] WIP: standard device classes, added GSI binding plus tests. --- src/db/db/db.pro | 4 +- src/db/db/gsiDeclDbNetlistDeviceClasses.cc | 232 +++++++++++++ src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 0 src/rba/unit_tests/rba.cc | 1 + testdata/ruby/dbNetlistDeviceClasses.rb | 331 +++++++++++++++++++ 5 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 src/db/db/gsiDeclDbNetlistDeviceClasses.cc create mode 100644 src/db/db/gsiDeclDbNetlistDeviceExtractor.cc create mode 100644 testdata/ruby/dbNetlistDeviceClasses.rb diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 65b36ade9..25cbb07b3 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -147,7 +147,9 @@ SOURCES = \ gsiDeclDbNetlist.cc \ dbNetlistDeviceClasses.cc \ dbNetlistDeviceExtractor.cc \ - dbNetlistExtractor.cc + dbNetlistExtractor.cc \ + gsiDeclDbNetlistDeviceClasses.cc \ + gsiDeclDbNetlistDeviceExtractor.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/gsiDeclDbNetlistDeviceClasses.cc b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc new file mode 100644 index 000000000..8ce4a554c --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc @@ -0,0 +1,232 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "gsiDecl.h" +#include "dbNetlistDeviceClasses.h" + +namespace gsi +{ + +// TODO: move this to gsiMethods.h + +template +class ConstantValueGetter + : public StaticMethodBase +{ +public: + ConstantValueGetter (const std::string &name, const R &v, const std::string &doc) + : StaticMethodBase (name, doc, true), m_v (v) + { + } + + void initialize () + { + this->clear (); + // Note: a constant must not return a reference to an existing object, hence "set_return_new": + this->template set_return_new (); + } + + virtual MethodBase *clone () const + { + return new ConstantValueGetter (*this); + } + + virtual void call (void *, SerialArgs &, SerialArgs &ret) const + { + mark_called (); + ret.write (m_v); + } + +private: + R m_v; +}; + +template +Methods +constant (const std::string &name, const R &v, const std::string &doc = std::string ()) +{ + return Methods (new ConstantValueGetter (name, v, doc)); +} + + +extern Class decl_dbDeviceClass; + +Class decl_dbDeviceClassResistor (decl_dbDeviceClass, "db", "DeviceClassResistor", + gsi::constant ("TERMINAL_A", db::DeviceClassResistor::terminal_id_A, + "@brief A constant giving the terminal ID for terminal A" + ) + + gsi::constant ("TERMINAL_B", db::DeviceClassResistor::terminal_id_B, + "@brief A constant giving the terminal ID for terminal B" + ) + + gsi::constant ("PARAM_R", db::DeviceClassResistor::param_id_R, + "@brief A constant giving the parameter ID for parameter R" + ), + "@brief A device class for a resistor.\n" + "This class can be used to describe resistors. Resistors are defined by their combination behavior and " + "the basic parameter 'R' which is the resistance in Ohm.\n" + "\n" + "A resistor has two terminals, A and B.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbDeviceClassCapacitor (decl_dbDeviceClass, "db", "DeviceClassCapacitor", + gsi::constant ("TERMINAL_A", db::DeviceClassCapacitor::terminal_id_A, + "@brief A constant giving the terminal ID for terminal A" + ) + + gsi::constant ("TERMINAL_B", db::DeviceClassCapacitor::terminal_id_B, + "@brief A constant giving the terminal ID for terminal B" + ) + + gsi::constant ("PARAM_C", db::DeviceClassCapacitor::param_id_C, + "@brief A constant giving the parameter ID for parameter C" + ), + "@brief A device class for a capacitor.\n" + "This class can be used to describe capacitors. Capacitors are defined by their combination behavior and " + "the basic parameter 'C' which is the capacitance in Farad.\n" + "\n" + "A capacitor has two terminals, A and B.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbDeviceClassInductor (decl_dbDeviceClass, "db", "DeviceClassInductor", + gsi::constant ("TERMINAL_A", db::DeviceClassInductor::terminal_id_A, + "@brief A constant giving the terminal ID for terminal A" + ) + + gsi::constant ("TERMINAL_B", db::DeviceClassInductor::terminal_id_B, + "@brief A constant giving the terminal ID for terminal B" + ) + + gsi::constant ("PARAM_L", db::DeviceClassInductor::param_id_L, + "@brief A constant giving the parameter ID for parameter L" + ), + "@brief A device class for an inductor.\n" + "This class can be used to describe inductors. Inductors are defined by their combination behavior and " + "the basic parameter 'L' which is the inductance in Henry.\n" + "\n" + "An inductor has two terminals, A and B.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbDeviceClassDiode (decl_dbDeviceClass, "db", "DeviceClassDiode", + gsi::constant ("TERMINAL_A", db::DeviceClassDiode::terminal_id_A, + "@brief A constant giving the terminal ID for terminal A" + ) + + gsi::constant ("TERMINAL_C", db::DeviceClassDiode::terminal_id_C, + "@brief A constant giving the terminal ID for terminal C" + ) + + gsi::constant ("PARAM_A", db::DeviceClassDiode::param_id_A, + "@brief A constant giving the parameter ID for parameter A" + ), + "@brief A device class for a diode.\n" + "This class can be used to describe diodes. Diodes are defined by their combination behavior and " + "the basic parameter 'A' which is their area in square micrometers.\n" + "\n" + "Diodes only combine when parallel and in the same direction. In this case, their areas are added." + "\n" + "An inductor has two terminals, A (anode) and C (cathode).\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbDeviceClassMOS3Transistor (decl_dbDeviceClass, "db", "DeviceClassMOS3Transistor", + gsi::constant ("TERMINAL_S", db::DeviceClassMOS3Transistor::terminal_id_S, + "@brief A constant giving the terminal ID for terminal S" + ) + + gsi::constant ("TERMINAL_D", db::DeviceClassMOS3Transistor::terminal_id_D, + "@brief A constant giving the terminal ID for terminal D" + ) + + gsi::constant ("TERMINAL_G", db::DeviceClassMOS3Transistor::terminal_id_G, + "@brief A constant giving the terminal ID for terminal G" + ) + + gsi::constant ("PARAM_L", db::DeviceClassMOS3Transistor::param_id_L, + "@brief A constant giving the parameter ID for parameter L" + ) + + gsi::constant ("PARAM_W", db::DeviceClassMOS3Transistor::param_id_W, + "@brief A constant giving the parameter ID for parameter W" + ) + + gsi::constant ("PARAM_AS", db::DeviceClassMOS3Transistor::param_id_AS, + "@brief A constant giving the parameter ID for parameter AS" + ) + + gsi::constant ("PARAM_AD", db::DeviceClassMOS3Transistor::param_id_AD, + "@brief A constant giving the parameter ID for parameter AD" + ), + "@brief A device class for a 3-terminal MOS transistor.\n" + "This class can be used to describe MOS transistors without a bulk terminal. " + "A device class for a MOS transistor with a bulk terminal is \\DeviceClassMOS4Transistor. " + "MOS transistors are defined by their combination behavior and the basic parameters.\n" + "\n" + "The parameters are L, W, AS and AD for the gate length and width in micrometers and source and drain area " + "in square micrometers.\n" + "\n" + "The terminals are S, G and D for source, gate and drain.\n" + "\n" + "MOS transistors combine in parallel mode, when both gate lengths are identical and " + "their gates are connected (source and drain can be swapped). In this case, their widths and source and drain " + "areas are added.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbDeviceClassMOS4Transistor (decl_dbDeviceClass, "db", "DeviceClassMOS4Transistor", + gsi::constant ("TERMINAL_S", db::DeviceClassMOS4Transistor::terminal_id_S, + "@brief A constant giving the terminal ID for terminal S" + ) + + gsi::constant ("TERMINAL_D", db::DeviceClassMOS4Transistor::terminal_id_D, + "@brief A constant giving the terminal ID for terminal D" + ) + + gsi::constant ("TERMINAL_G", db::DeviceClassMOS4Transistor::terminal_id_G, + "@brief A constant giving the terminal ID for terminal G" + ) + + gsi::constant ("TERMINAL_B", db::DeviceClassMOS4Transistor::terminal_id_B, + "@brief A constant giving the terminal ID for terminal B" + ) + + gsi::constant ("PARAM_L", db::DeviceClassMOS4Transistor::param_id_L, + "@brief A constant giving the parameter ID for parameter L" + ) + + gsi::constant ("PARAM_W", db::DeviceClassMOS4Transistor::param_id_W, + "@brief A constant giving the parameter ID for parameter W" + ) + + gsi::constant ("PARAM_AS", db::DeviceClassMOS4Transistor::param_id_AS, + "@brief A constant giving the parameter ID for parameter AS" + ) + + gsi::constant ("PARAM_AD", db::DeviceClassMOS4Transistor::param_id_AD, + "@brief A constant giving the parameter ID for parameter AD" + ), + "@brief A device class for a 4-terminal MOS transistor.\n" + "This class can be used to describe MOS transistors with a bulk terminal. " + "A device class for a MOS transistor without a bulk terminal is \\DeviceClassMOS3Transistor. " + "MOS transistors are defined by their combination behavior and the basic parameters.\n" + "\n" + "The parameters are L, W, AS and AD for the gate length and width in micrometers and source and drain area " + "in square micrometers.\n" + "\n" + "The terminals are S, G, D and B for source, gate, drain and bulk.\n" + "\n" + "MOS transistors combine in parallel mode, when both gate lengths are identical and " + "their gates and bulk terminals are connected (source and drain can be swapped). In this case, their widths and source and drain " + "areas are added.\n" + "\n" + "This class has been introduced in version 0.26." +); + +} diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc new file mode 100644 index 000000000..e69de29bb diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index 51020ae1e..2df8e38bb 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -110,6 +110,7 @@ RUBYTEST (dbLayoutDiff, "dbLayoutDiff.rb") RUBYTEST (dbLayoutQuery, "dbLayoutQuery.rb") RUBYTEST (dbMatrix, "dbMatrix.rb") RUBYTEST (dbNetlist, "dbNetlist.rb") +RUBYTEST (dbNetlistDeviceClasses, "dbNetlistDeviceClasses.rb") RUBYTEST (dbPathTest, "dbPathTest.rb") RUBYTEST (dbPCells, "dbPCells.rb") RUBYTEST (dbPointTest, "dbPointTest.rb") diff --git a/testdata/ruby/dbNetlistDeviceClasses.rb b/testdata/ruby/dbNetlistDeviceClasses.rb new file mode 100644 index 000000000..cfd51b421 --- /dev/null +++ b/testdata/ruby/dbNetlistDeviceClasses.rb @@ -0,0 +1,331 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2018 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBNetlist_TestClass < TestBase + + def test_1_Resistors + + cls = RBA::DeviceClassResistor::new + + nl = RBA::Netlist::new + nl.add(cls) + + circuit = RBA::Circuit::new + nl.add(circuit) + + r1 = circuit.create_device(cls, "r1") + r1.set_parameter(RBA::DeviceClassResistor::PARAM_R, 1.0) + r2 = circuit.create_device(cls, "r2") + r2.set_parameter("R", 3.0) + + pin_a = circuit.create_pin ("A") + pin_b = circuit.create_pin ("B") + + n1 = circuit.create_net("n1") + circuit.connect_pin(pin_a, n1) + r1.connect_terminal(RBA::DeviceClassResistor::TERMINAL_A, n1) + + n2 = circuit.create_net("n2") + r1.connect_terminal(RBA::DeviceClassResistor::TERMINAL_B, n2) + r2.connect_terminal(RBA::DeviceClassResistor::TERMINAL_A, n2) + + n3 = circuit.create_net("n3") + r2.connect_terminal(RBA::DeviceClassResistor::TERMINAL_B, n3) + circuit.connect_pin(pin_b, n3) + + assert_equal(nl.to_s, < Date: Sun, 30 Dec 2018 11:32:33 +0100 Subject: [PATCH 146/335] WIP: GSI binding of device extractor classes. --- src/db/db/db.pro | 3 +- src/db/db/dbHierNetworkProcessor.h | 10 +- src/db/db/dbNetlistDeviceExtractor.cc | 23 +- src/db/db/dbNetlistDeviceExtractor.h | 53 ++- src/db/db/gsiDeclDbHierNetworkProcessor.cc | 51 +++ src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 371 ++++++++++++++++++ .../dbNetlistDeviceExtractorTests.cc | 20 +- 7 files changed, 483 insertions(+), 48 deletions(-) create mode 100644 src/db/db/gsiDeclDbHierNetworkProcessor.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 25cbb07b3..dde3156b3 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -149,7 +149,8 @@ SOURCES = \ dbNetlistDeviceExtractor.cc \ dbNetlistExtractor.cc \ gsiDeclDbNetlistDeviceClasses.cc \ - gsiDeclDbNetlistDeviceExtractor.cc + gsiDeclDbNetlistDeviceExtractor.cc \ + gsiDeclDbHierNetworkProcessor.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 87b957f23..d91b4d287 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -58,6 +58,11 @@ public: */ Connectivity (); + /** + * @brief Adds intra-layer connectivity for layer l + */ + void connect (unsigned int l); + /** * @brief Adds inter-layer connectivity */ @@ -77,11 +82,6 @@ public: */ void connect (const db::DeepLayer &la, const db::DeepLayer &lb); - /** - * @brief Adds intra-layer connectivity for layer l - */ - void connect (unsigned int l); - /** * @brief Begin iterator for the layers involved */ diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index f0ce1a7f3..210351de2 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -218,6 +218,9 @@ void NetlistDeviceExtractor::register_device_class (DeviceClass *device_class) if (mp_device_class != 0) { throw tl::Exception (tl::to_string (tr ("Device class already set"))); } + if (m_name.empty ()) { + throw tl::Exception (tl::to_string (tr ("No device extractor/device class name set"))); + } tl_assert (device_class != 0); mp_device_class = device_class; @@ -286,16 +289,10 @@ void NetlistDeviceExtractor::error (const std::string &msg) m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); } -void NetlistDeviceExtractor::error (const std::string &msg, const db::Polygon &poly) +void NetlistDeviceExtractor::error (const std::string &msg, const db::DPolygon &poly) { error (msg); - m_errors.back ().set_geometry (db::Region (poly)); -} - -void NetlistDeviceExtractor::error (const std::string &msg, const db::Region ®ion) -{ - error (msg); - m_errors.back ().set_geometry (region); + m_errors.back ().set_geometry (poly); } void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg) @@ -305,16 +302,10 @@ void NetlistDeviceExtractor::error (const std::string &category_name, const std: m_errors.back ().set_category_description (category_description); } -void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Polygon &poly) +void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::DPolygon &poly) { error (category_name, category_description, msg); - m_errors.back ().set_geometry (db::Region (poly)); -} - -void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Region ®ion) -{ - error (category_name, category_description, msg); - m_errors.back ().set_geometry (region); + m_errors.back ().set_geometry (poly); } } diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index 871fc27a1..ab089c80d 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -91,9 +91,9 @@ public: /** * @brief Gets the geometry for this error - * Not all errors may specify a geometry. + * Not all errors may specify a geometry. In this case, the polygon is empty. */ - const db::Region &geometry () const + const db::DPolygon &geometry () const { return m_geometry; } @@ -101,7 +101,7 @@ public: /** * @brief Sets the geometry */ - void set_geometry (const db::Region &g) + void set_geometry (const db::DPolygon &g) { m_geometry = g; } @@ -141,7 +141,7 @@ public: private: std::string m_cell_name; std::string m_message; - db::Region m_geometry; + db::DPolygon m_geometry; std::string m_category_name, m_category_description; }; @@ -293,14 +293,21 @@ public: } protected: + /** + * @brief Sets the name of the device class and the device extractor + */ + void set_name (const std::string &name) + { + m_name = name; + } + /** * @brief Sets up the extractor * * This method is supposed to set up the device extractor. This involves two basic steps: - * defining the device classes and setting up the device layers. + * defining the device classe and setting up the device layers. * - * At least one device class needs to be defined. Use "register_device_class" to register - * the device class you need. + * Use "register_device_class" to register the device class you need. * * The device layers need to be defined by calling "define_layer" once or several times. */ @@ -340,7 +347,7 @@ protected: /** * @brief Defines a layer * Each call will define one more layer for the device extraction. - * This method shall be used inside the implementation of "setip" to define + * This method shall be used inside the implementation of "setup" to define * the device layers. The actual geometries are later available to "extract_devices" * in the order the layers are defined. */ @@ -411,12 +418,15 @@ protected: /** * @brief Issues an error with the given message and error shape */ - void error (const std::string &msg, const db::Polygon &poly); + void error (const std::string &msg, const db::DPolygon &poly); /** - * @brief Issues an error with the given message and error geometry + * @brief Issues an error with the given message and error shape */ - void error (const std::string &msg, const db::Region ®ion); + void error (const std::string &msg, const db::Polygon &poly) + { + error (msg, poly.transformed (db::CplxTrans (dbu ()))); + } /** * @brief Issues an error with the given category name, description and message @@ -426,12 +436,15 @@ protected: /** * @brief Issues an error with the given category name, description and message and error shape */ - void error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Polygon &poly); + void error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::DPolygon &poly); /** - * @brief Issues an error with the given category name, description and message and error geometry + * @brief Issues an error with the given category name, description and message and error shape */ - void error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Region ®ion); + void error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Polygon &poly) + { + error (category_name, category_description, msg, poly.transformed (db::CplxTrans (dbu ()))); + } /** * @brief Gets the name of the current cell @@ -465,4 +478,16 @@ private: } +namespace tl +{ + +template<> struct tl::type_traits : public tl::type_traits +{ + // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + #endif diff --git a/src/db/db/gsiDeclDbHierNetworkProcessor.cc b/src/db/db/gsiDeclDbHierNetworkProcessor.cc new file mode 100644 index 000000000..4514f0084 --- /dev/null +++ b/src/db/db/gsiDeclDbHierNetworkProcessor.cc @@ -0,0 +1,51 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "gsiDecl.h" +#include "dbHierNetworkProcessor.h" + +namespace gsi +{ + +Class decl_dbConnectivity ("db", "Connectivity", + gsi::method ("connect", (void (db::Connectivity::*) (unsigned int)) &db::Connectivity::connect, gsi::arg ("layer"), + "@brief Specifies intra-layer connectivity.\n" + ) + + gsi::method ("connect", (void (db::Connectivity::*) (unsigned int, unsigned int)) &db::Connectivity::connect, gsi::arg ("layer_a"), gsi::arg ("layer_b"), + "@brief Specifies inter-layer connectivity.\n" + ), + "@brief This class specifies connections between different layers." + "Connections are build using \\connect. There are basically two flavours of connections: intra-layer and inter-layer.\n" + "\n" + "Intra-layer connections make nets begin propagated along different shapes on the same net. Without the " + "intra-layer connections, nets are not propagated over shape boundaries. As this is usually intended, intra-layer connections " + "should always be specified for each layer.\n" + "\n" + "Inter-layer connections connect shapes on different layers. Shapes which touch across layers will be connected if " + "their layers are specified as being connected through inter-layer \\connect.\n" + "\n" + "All layers are specified in terms of layer indexes. Layer indexes are layout layer indexes (see \\Layout class).\n" + "\n" + "This class has been introduced in version 0.26.\n" +); + +} diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index e69de29bb..87fe8df86 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -0,0 +1,371 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "gsiDecl.h" +#include "dbNetlistDeviceExtractor.h" + +namespace { + +/** + * @brief A NetlistDeviceExtractor implementation that allows reimplementation of the virtual methods + */ +class NetlistDeviceExtractorImpl + : public db::NetlistDeviceExtractor +{ +public: + NetlistDeviceExtractorImpl () + : db::NetlistDeviceExtractor (std::string ()) + { + // .. nothing yet .. + } + + using db::NetlistDeviceExtractor::set_name; + using db::NetlistDeviceExtractor::define_layer; + using db::NetlistDeviceExtractor::define_terminal; + using db::NetlistDeviceExtractor::create_device; + using db::NetlistDeviceExtractor::dbu; + using db::NetlistDeviceExtractor::layout; + using db::NetlistDeviceExtractor::cell_index; + using db::NetlistDeviceExtractor::cell_name; + using db::NetlistDeviceExtractor::error; + using db::NetlistDeviceExtractor::get_connectivity; + using db::NetlistDeviceExtractor::extract_devices; + + void register_device_class (db::DeviceClass *device_class) + { + // the class is owned by the extractor + device_class->keep (); + db::NetlistDeviceExtractor::register_device_class (device_class); + } + + void setup_fb () + { + return db::NetlistDeviceExtractor::setup (); + } + + virtual void setup () + { + if (cb_setup.can_issue ()) { + cb_setup.issue (&NetlistDeviceExtractorImpl::setup_fb); + } else { + db::NetlistDeviceExtractor::setup (); + } + } + + db::Connectivity get_connectivity_fb (const db::Layout &layout, const std::vector &layers) const + { + return db::NetlistDeviceExtractor::get_connectivity (layout, layers); + } + + virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector &layers) const + { + if (cb_get_connectivity.can_issue ()) { + return cb_get_connectivity.issue &> (&NetlistDeviceExtractorImpl::get_connectivity_fb, layout, layers); + } else { + return db::NetlistDeviceExtractor::get_connectivity (layout, layers); + } + } + + void extract_devices_fb (const std::vector &layer_geometry) + { + return db::NetlistDeviceExtractor::extract_devices (layer_geometry); + } + + virtual void extract_devices (const std::vector &layer_geometry) + { + if (cb_extract_devices.can_issue ()) { + cb_extract_devices.issue &> (&NetlistDeviceExtractorImpl::extract_devices_fb, layer_geometry); + } else { + db::NetlistDeviceExtractor::extract_devices (layer_geometry); + } + } + + gsi::Callback cb_setup; + gsi::Callback cb_get_connectivity; + gsi::Callback cb_extract_devices; +}; + +} + +namespace tl +{ + +template<> struct tl::type_traits : public tl::type_traits +{ + // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + +namespace gsi +{ + +Class decl_dbNetlistDeviceExtractorError ("db", "NetlistDeviceExtractorError", + gsi::method ("message", &db::NetlistDeviceExtractorError::message, + "@brief Gets the message text.\n" + ) + + gsi::method ("message=", &db::NetlistDeviceExtractorError::set_message, gsi::arg ("message"), + "@brief Sets the message text.\n" + ) + + gsi::method ("cell_name", &db::NetlistDeviceExtractorError::cell_name, + "@brief Gets the cell name.\n" + "See \\cell_name= for details about this attribute." + ) + + gsi::method ("cell_name=", &db::NetlistDeviceExtractorError::set_cell_name, gsi::arg ("cell_name"), + "@brief Sets the cell name.\n" + "The cell name is the name of the layout cell which was treated. This is " + "also the name of the circuit the device should have appeared in (it may be dropped because of this error). " + "If netlist hierarchy manipulation happens however, the circuit may not exist " + "any longer or may be renamed." + ) + + gsi::method ("geometry", &db::NetlistDeviceExtractorError::geometry, + "@brief Gets the geometry.\n" + "See \\geometry= for more details." + ) + + gsi::method ("geometry=", &db::NetlistDeviceExtractorError::set_geometry, gsi::arg ("polygon"), + "@brief Sets the geometry.\n" + "The geometry is optional. If given, a marker will be shown when selecting this error." + ) + + gsi::method ("category_name", &db::NetlistDeviceExtractorError::category_name, + "@brief Gets the category name.\n" + "See \\category_name= for more details." + ) + + gsi::method ("category_name=", &db::NetlistDeviceExtractorError::set_category_name, gsi::arg ("name"), + "@brief Sets the category name.\n" + "The category name is optional. If given, it specifies a formal category name. Errors with the same " + "category name are shown in that category. If in addition a category description is specified " + "(see \\category_description), this description will be displayed as the title of." + ) + + gsi::method ("category_description", &db::NetlistDeviceExtractorError::category_description, + "@brief Gets the category description.\n" + "See \\category_name= for details about categories." + ) + + gsi::method ("category_description=", &db::NetlistDeviceExtractorError::set_category_description, gsi::arg ("description"), + "@brief Sets the category description.\n" + "See \\category_name= for details about categories." + ), + "@brief An error that occured during device extraction\n" + "The device extractor will keep errors that occured during extraction of the devices. " + "It does not by using this error class.\n" + "\n" + "An error is basically described by the cell/circuit it occures in and the message. " + "In addition, a geometry may be attached forming a marker that can be shown when the error is selected. " + "The geometry is given as a \\DPolygon object. If no geometry is specified, this polygon is empty.\n" + "\n" + "For categorization of the errors, a category name and description may be specified. If given, the " + "errors will be shown in the specified category. The category description is optional.\n" + "\n" + "This class has been introduced in version 0.26." +); + +static const std::string &ld_name (const db::NetlistDeviceExtractorLayerDefinition *ld) +{ + return ld->name; +} + +static const std::string &ld_description (const db::NetlistDeviceExtractorLayerDefinition *ld) +{ + return ld->description; +} + +static int ld_index (const db::NetlistDeviceExtractorLayerDefinition *ld) +{ + return ld->index; +} + +Class decl_dbNetlistDeviceExtractorLayerDefinition ("db", "NetlistDeviceExtractorLayerDefinition", + gsi::method_ext ("name", &ld_name, + "@brief Gets the name of the layer.\n" + ) + + gsi::method_ext ("description", &ld_description, + "@brief Gets the description of the layer.\n" + ) + + gsi::method_ext ("index", &ld_index, + "@brief Gets the index of the layer.\n" + ), + "@brief Describes a layer used in the device extraction\n" + "This read-only structure is used to describe a layer in the device extraction.\n" + "Every device has specific layers used in the device extraction process.\n" + "Layer definitions can be retrieved using \\NetlistDeviceExtractor#each_layer.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbNetlistDeviceExtractor ("db", "NetlistDeviceExtractorImpl", + gsi::method ("name", &NetlistDeviceExtractorImpl::name, + "@brief Gets the name of the device extractor and the device class." + ) + + gsi::method ("name=", &NetlistDeviceExtractorImpl::set_name, + "@brief Sets the name of the device extractor and the device class." + ) + + gsi::iterator ("each_layer_definition", &NetlistDeviceExtractorImpl::begin_layer_definitions, &NetlistDeviceExtractorImpl::end_layer_definitions, + "@brief Iterates over all layer definitions." + ) + + gsi::callback ("setup", &NetlistDeviceExtractorImpl::setup, &NetlistDeviceExtractorImpl::cb_setup, + "@brief Sets up the extractor.\n" + "This method is supposed to set up the device extractor. This involves three basic steps:\n" + "defining the name, the device classe and setting up the device layers.\n" + "\n" + "Use \\name= to give the extractor and it's device class a name.\n" + "Use \\register_device_class to register the device class you need.\n" + "Defined the layers by calling \\define_layer once or several times.\n" + ) + + gsi::callback ("get_connectivity", &NetlistDeviceExtractorImpl::get_connectivity, &NetlistDeviceExtractorImpl::cb_get_connectivity, + gsi::arg ("layout"), gsi::arg ("layers"), + "@brief Gets the connectivity object used to extract the device geometry.\n" + "This method shall raise an error, if the input layer are not properly defined (e.g.\n" + "too few etc.)\n" + "\n" + "The 'layers' argument specifies the actual layer layouts for the logical device layers (see \\define_layer). " + "The list of layers corresponds to the number of layers defined. Use the layer indexes from this list " + "to build the connectivity with \\Connectivity#connect." + ) + + gsi::callback ("extract_devices", &NetlistDeviceExtractorImpl::extract_devices, &NetlistDeviceExtractorImpl::cb_extract_devices, + gsi::arg ("layer_geometry"), + "@brief Extracts the devices from the given shape cluster.\n" + "\n" + "The shape cluster is a set of geometries belonging together in terms of the\n" + "connectivity defined by \"get_connectivity\". The cluster might cover multiple devices,\n" + "so the implementation needs to consider this case. The geometries are already merged.\n" + "\n" + "The implementation of this method shall use \"create_device\" to create new\n" + "devices based on the geometry found. It shall use \"define_terminal\" to define\n" + "terminals by which the nets extracted in the network extraction step connect\n" + "to the new devices.\n" + ) + + gsi::method ("register_device_class", &NetlistDeviceExtractorImpl::register_device_class, gsi::arg ("device_class"), + "@brief Registers a device class.\n" + "The device class object will become owned by the netlist and must not be deleted by\n" + "the caller. The name of the device class will be changed to the name given to\n" + "the device extractor.\n" + "This method shall be used inside the implementation of \\setup to register\n" + "the device classes.\n" + ) + + gsi::method ("define_layer", &NetlistDeviceExtractorImpl::define_layer, gsi::arg ("name"), gsi::arg ("description"), + "@brief Defines a layer.\n" + "Each call will define one more layer for the device extraction.\n" + "This method shall be used inside the implementation of \\setup to define\n" + "the device layers. The actual geometries are later available to \\extract_devices\n" + "in the order the layers are defined.\n" + ) + + gsi::method ("create_device", &NetlistDeviceExtractorImpl::create_device, + "@brief Creates a device.\n" + "The device object returned can be configured by the caller, e.g. set parameters.\n" + "It will be owned by the netlist and must not be deleted by the caller.\n" + ) + + gsi::method ("define_terminal", (void (NetlistDeviceExtractorImpl::*) (db::Device *, size_t, size_t, const db::Polygon &)) &NetlistDeviceExtractorImpl::define_terminal, + gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("shape"), + "@brief Defines a device terminal.\n" + "This method will define a terminal to the given device and the given terminal ID. \n" + "The terminal will be placed on the layer given by \"layer_index\". The layer index \n" + "is the index of the layer during layer definition. The first layer is 0, the second layer 1 etc.\n" + "\n" + "This version produces a terminal with a shape given by the polygon. Note that the polygon is\n" + "specified in database units.\n" + ) + + gsi::method ("define_terminal", (void (NetlistDeviceExtractorImpl::*) (db::Device *, size_t, size_t, const db::Box &)) &NetlistDeviceExtractorImpl::define_terminal, + gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("shape"), + "@brief Defines a device terminal.\n" + "This method will define a terminal to the given device and the given terminal ID. \n" + "The terminal will be placed on the layer given by \"layer_index\". The layer index \n" + "is the index of the layer during layer definition. The first layer is 0, the second layer 1 etc.\n" + "\n" + "This version produces a terminal with a shape given by the box. Note that the box is\n" + "specified in database units.\n" + ) + + gsi::method ("define_terminal", (void (NetlistDeviceExtractorImpl::*) (db::Device *, size_t, size_t, const db::Point &)) &NetlistDeviceExtractorImpl::define_terminal, + gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("point"), + "@brief Defines a device terminal.\n" + "This method will define a terminal to the given device and the given terminal ID. \n" + "The terminal will be placed on the layer given by \"layer_index\". The layer index \n" + "is the index of the layer during layer definition. The first layer is 0, the second layer 1 etc.\n" + "\n" + "This version produces a point-like terminal. Note that the point is\n" + "specified in database units.\n" + ) + + gsi::method ("dbu", &NetlistDeviceExtractorImpl::dbu, + "@brief Gets the database unit\n" + ) + + gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &)) &NetlistDeviceExtractorImpl::error, + gsi::arg ("message"), + "@brief Issues an error with the given message\n" + ) + + gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const db::DPolygon &)) &NetlistDeviceExtractorImpl::error, + gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues an error with the given message and micrometer-units polygon geometry\n" + ) + + gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const db::Polygon &)) &NetlistDeviceExtractorImpl::error, + gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues an error with the given message and databse-unit polygon geometry\n" + ) + + gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const std::string &, const std::string &)) &NetlistDeviceExtractorImpl::error, + gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), + "@brief Issues an error with the given category name and description, message\n" + ) + + gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const std::string &, const std::string &, const db::DPolygon &)) &NetlistDeviceExtractorImpl::error, + gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues an error with the given category name and description, message and micrometer-units polygon geometry\n" + ) + + gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const std::string &, const std::string &, const db::Polygon &)) &NetlistDeviceExtractorImpl::error, + gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues an error with the given category name and description, message and databse-unit polygon geometry\n" + ), + "@brief The basic class for implementing custom device extractors.\n" + "\n" + "This class serves as a base class for implementing customized device extractors. " + "This class does not provide any extraction functionality, so you have to " + "implement every detail.\n" + "\n" + "Device extraction requires a few definitions. The definitions are made in the reimplementation of the \\setup\n" + "method. Required definitions to be made are:\n" + "\n" + "@ul\n" + " @li The name of the extractor. This will also be the name of the device class produced by the extractor. " + " The name is set using \\name=. @/li\n" + " @li The device class of the devices to produce. The device class is registered using \\register_device_class. @/li\n" + " @li The layers used for the device extraction. These are input layers for the extraction as well as " + " output layers for defining the terminals. Terminals are the poins at which the nets connect to the devices.\n" + " Layers are defined using \\define_layer. Initially, layers are abstract definitions with a name and a description.\n" + " Concrete layers will be given when defining the connectivitiy. @/li\n" + "@/ul\n" + "\n" + "When the device extraction is started, the device extraction algorithm will first ask the device extractor " + "for the 'connectivity'. This is not a connectivity in a sense of electrical connections. The connectivity defines are " + "logical compound that makes up the device. 'Connected' shapes are collected and presented to the device extractor.\n" + "The connectivity is obtained by calling \\get_connectivity. This method must be " + "implemented to produce the connectivity.\n" + "\n" + "Finally, the individual devices need to be extracted. Each cluster of connected shapes is presented to the " + "device extractor. A cluster may include more than one device. It's the device extractor's responsibilty to " + "extract the devices from this cluster and deliver the devices through \\create_device. In addition, terminals " + "have to be defined, so the net extractor can connect to the devices. Terminal definitions are made through " + "\\define_terminal. The device extraction is implemented in the \\extract_devices method.\n" + "\n" + "If errors occur during device extraction, the \\error method may be used to issue such errors. Errors " + "reported this way are kept in the error log.\n" + "\n" + "This class has been introduced in version 0.26." +); + +} diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index f8450667b..a11c14d49 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -37,7 +37,7 @@ TEST(1_NetlistDeviceExtractorErrorBasic) EXPECT_EQ (error.category_description (), "cdesc"); error.set_cell_name ("cell"); EXPECT_EQ (error.cell_name (), "cell"); - error.set_geometry (db::Region (db::Box (0, 1, 2, 3))); + error.set_geometry (db::DPolygon (db::DBox (0, 1, 2, 3))); EXPECT_EQ (error.geometry ().to_string (), "(0,1;0,3;2,3;2,1)"); error = db::NetlistDeviceExtractorError ("cell2", "msg2"); @@ -45,7 +45,7 @@ TEST(1_NetlistDeviceExtractorErrorBasic) EXPECT_EQ (error.message (), "msg2"); EXPECT_EQ (error.category_name (), ""); EXPECT_EQ (error.category_description (), ""); - EXPECT_EQ (error.geometry ().to_string (), ""); + EXPECT_EQ (error.geometry ().to_string (), "()"); } namespace { @@ -57,11 +57,9 @@ namespace { : db::NetlistDeviceExtractor (std::string ("DUMMY")) { error ("msg1"); - error ("msg2", db::Box (0, 1, 2, 3)); - error ("msg3", db::Region (db::Box (10, 11, 12, 13))); + error ("msg2", db::DPolygon (db::DBox (0, 1, 2, 3))); error ("cat1", "desc1", "msg1"); - error ("cat1", "desc1", "msg2", db::Box (0, 1, 2, 3)); - error ("cat1", "desc1", "msg3", db::Region (db::Box (10, 11, 12, 13))); + error ("cat1", "desc1", "msg3", db::DPolygon (db::DBox (10, 11, 12, 13))); } }; } @@ -79,11 +77,9 @@ TEST(2_NetlistDeviceExtractorErrors) EXPECT_EQ (dummy_ex.has_errors (), true); std::vector errors (dummy_ex.begin_errors (), dummy_ex.end_errors ()); - EXPECT_EQ (int (errors.size ()), 6); - EXPECT_EQ (error2string (errors [0]), "::::msg1"); + EXPECT_EQ (int (errors.size ()), 4); + EXPECT_EQ (error2string (errors [0]), ":::():msg1"); EXPECT_EQ (error2string (errors [1]), ":::(0,1;0,3;2,3;2,1):msg2"); - EXPECT_EQ (error2string (errors [2]), ":::(10,11;10,13;12,13;12,11):msg3"); - EXPECT_EQ (error2string (errors [3]), ":cat1:desc1::msg1"); - EXPECT_EQ (error2string (errors [4]), ":cat1:desc1:(0,1;0,3;2,3;2,1):msg2"); - EXPECT_EQ (error2string (errors [5]), ":cat1:desc1:(10,11;10,13;12,13;12,11):msg3"); + EXPECT_EQ (error2string (errors [2]), ":cat1:desc1:():msg1"); + EXPECT_EQ (error2string (errors [3]), ":cat1:desc1:(10,11;10,13;12,13;12,11):msg3"); } From 293c6f496efa1e735fa28752d5924f3f9550634c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Dec 2018 13:00:03 +0100 Subject: [PATCH 147/335] WIP: more query functions for netlist classes (i.e. net by name, device by name etc.), some refactoring, GSI bindings, tests. --- src/db/db/dbNetlist.cc | 146 ++++++++-------- src/db/db/dbNetlist.h | 254 ++++++++++++++++++++++++++-- src/db/db/gsiDeclDbNetlist.cc | 28 +++ src/db/unit_tests/dbNetlistTests.cc | 68 +++++++- testdata/ruby/dbNetlist.rb | 23 +++ 5 files changed, 435 insertions(+), 84 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 3c378a4b0..d301a75b9 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -89,6 +89,9 @@ void Device::set_circuit (Circuit *circuit) void Device::set_name (const std::string &n) { m_name = n; + if (mp_circuit) { + mp_circuit->m_device_by_name.invalidate (); + } } void Device::set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter) @@ -220,6 +223,9 @@ SubCircuit &SubCircuit::operator= (const SubCircuit &other) void SubCircuit::set_name (const std::string &n) { m_name = n; + if (mp_circuit) { + mp_circuit->m_subcircuit_by_name.invalidate (); + } } void SubCircuit::set_trans (const db::DCplxTrans &t) @@ -435,6 +441,9 @@ void Net::clear () void Net::set_name (const std::string &name) { m_name = name; + if (mp_circuit) { + mp_circuit->m_net_by_name.invalidate (); + } } std::string Net::expanded_name () const @@ -454,6 +463,9 @@ std::string Net::expanded_name () const void Net::set_cluster_id (size_t ci) { m_cluster_id = ci; + if (mp_circuit) { + mp_circuit->m_net_by_cluster_id.invalidate (); + } } void Net::add_pin (const NetPinRef &pin) @@ -510,13 +522,27 @@ void Net::set_circuit (Circuit *circuit) // Circuit class implementation Circuit::Circuit () - : mp_netlist (0), m_valid_device_id_table (false), m_valid_subcircuit_id_table (false), m_index (0) + : mp_netlist (0), + m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), + m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), + m_index (0) { // .. nothing yet .. } Circuit::Circuit (const Circuit &other) - : mp_netlist (0), m_valid_device_id_table (false), m_valid_subcircuit_id_table (false), m_index (0) + : mp_netlist (0), + m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), + m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), + m_index (0) { operator= (other); } @@ -526,8 +552,12 @@ Circuit &Circuit::operator= (const Circuit &other) if (this != &other) { m_name = other.m_name; - invalidate_device_id_table (); - invalidate_subcircuit_id_table (); + m_device_by_id.invalidate (); + m_subcircuit_by_id.invalidate (); + m_net_by_cluster_id.invalidate (); + m_device_by_name.invalidate (); + m_subcircuit_by_name.invalidate (); + m_net_by_name.invalidate (); for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { add_pin (*i); @@ -592,6 +622,16 @@ const Pin *Circuit::pin_by_id (size_t id) const } } +const Pin *Circuit::pin_by_name (const std::string &name) const +{ + for (Circuit::const_pin_iterator p = begin_pins (); p != end_pins (); ++p) { + if (p->name () == name) { + return p.operator-> (); + } + } + return 0; +} + void Circuit::clear () { m_name.clear (); @@ -599,20 +639,28 @@ void Circuit::clear () m_devices.clear (); m_nets.clear (); m_subcircuits.clear (); - m_device_id_table.clear (); - m_subcircuit_id_table.clear (); - m_valid_device_id_table = false; - m_valid_subcircuit_id_table = false; + m_device_by_id.invalidate (); + m_subcircuit_by_id.invalidate (); + m_net_by_cluster_id.invalidate (); + m_device_by_name.invalidate (); + m_subcircuit_by_name.invalidate (); + m_net_by_name.invalidate (); } void Circuit::set_name (const std::string &name) { m_name = name; + if (mp_netlist) { + mp_netlist->m_circuit_by_name.invalidate (); + } } void Circuit::set_cell_index (const db::cell_index_type ci) { m_cell_index = ci; + if (mp_netlist) { + mp_netlist->m_circuit_by_cell_index.invalidate (); + } } Circuit::child_circuit_iterator Circuit::begin_children () @@ -674,11 +722,15 @@ void Circuit::add_net (Net *net) { m_nets.push_back (net); net->set_circuit (this); + m_net_by_cluster_id.invalidate (); + m_net_by_name.invalidate (); } void Circuit::remove_net (Net *net) { m_nets.erase (net); + m_net_by_cluster_id.invalidate (); + m_net_by_name.invalidate (); } void Circuit::add_device (Device *device) @@ -693,39 +745,15 @@ void Circuit::add_device (Device *device) device->set_id (id + 1); m_devices.push_back (device); - invalidate_device_id_table (); + m_device_by_id.invalidate (); + m_device_by_name.invalidate (); } void Circuit::remove_device (Device *device) { m_devices.erase (device); - invalidate_device_id_table (); -} - -void Circuit::validate_device_id_table () -{ - m_device_id_table.clear (); - for (device_iterator d = begin_devices (); d != end_devices (); ++d) { - m_device_id_table.insert (std::make_pair (d->id (), d.operator-> ())); - } - - m_valid_device_id_table = true; -} - -void Circuit::invalidate_device_id_table () -{ - m_valid_device_id_table = false; - m_device_id_table.clear (); -} - -Device *Circuit::device_by_id (size_t id) -{ - if (! m_valid_device_id_table) { - validate_device_id_table (); - } - - std::map::const_iterator d = m_device_id_table.find (id); - return d != m_device_id_table.end () ? d->second : 0; + m_device_by_id.invalidate (); + m_device_by_name.invalidate (); } void Circuit::add_subcircuit (SubCircuit *subcircuit) @@ -740,7 +768,8 @@ void Circuit::add_subcircuit (SubCircuit *subcircuit) subcircuit->set_id (id + 1); m_subcircuits.push_back (subcircuit); - invalidate_subcircuit_id_table (); + m_subcircuit_by_id.invalidate (); + m_subcircuit_by_name.invalidate (); if (mp_netlist) { mp_netlist->invalidate_topology (); @@ -750,39 +779,14 @@ void Circuit::add_subcircuit (SubCircuit *subcircuit) void Circuit::remove_subcircuit (SubCircuit *subcircuit) { m_subcircuits.erase (subcircuit); - invalidate_subcircuit_id_table (); + m_subcircuit_by_id.invalidate (); + m_subcircuit_by_name.invalidate (); if (mp_netlist) { mp_netlist->invalidate_topology (); } } -void Circuit::validate_subcircuit_id_table () -{ - m_subcircuit_id_table.clear (); - for (subcircuit_iterator d = begin_subcircuits (); d != end_subcircuits (); ++d) { - m_subcircuit_id_table.insert (std::make_pair (d->id (), d.operator-> ())); - } - - m_valid_subcircuit_id_table = true; -} - -void Circuit::invalidate_subcircuit_id_table () -{ - m_valid_subcircuit_id_table = false; - m_subcircuit_id_table.clear (); -} - -SubCircuit *Circuit::subcircuit_by_id (size_t id) -{ - if (! m_valid_subcircuit_id_table) { - validate_subcircuit_id_table (); - } - - std::map::const_iterator d = m_subcircuit_id_table.find (id); - return d != m_subcircuit_id_table.end () ? d->second : 0; -} - void Circuit::register_ref (SubCircuit *r) { m_refs.push_back (r); @@ -1160,13 +1164,17 @@ size_t DeviceClass::terminal_id_for_name (const std::string &name) const // Netlist class implementation Netlist::Netlist () - : m_valid_topology (false), m_lock_count (0) + : m_valid_topology (false), m_lock_count (0), + m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), + m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits) { m_circuits.changed ().add (this, &Netlist::invalidate_topology); } Netlist::Netlist (const Netlist &other) - : m_valid_topology (false), m_lock_count (0) + : m_valid_topology (false), m_lock_count (0), + m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), + m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits) { operator= (other); m_circuits.changed ().add (this, &Netlist::invalidate_topology); @@ -1444,18 +1452,24 @@ void Netlist::clear () { m_device_classes.clear (); m_circuits.clear (); + m_circuit_by_name.invalidate (); + m_circuit_by_cell_index.invalidate (); } void Netlist::add_circuit (Circuit *circuit) { m_circuits.push_back (circuit); circuit->set_netlist (this); + m_circuit_by_name.invalidate (); + m_circuit_by_cell_index.invalidate (); } void Netlist::remove_circuit (Circuit *circuit) { circuit->set_netlist (0); m_circuits.erase (circuit); + m_circuit_by_name.invalidate (); + m_circuit_by_cell_index.invalidate (); } void Netlist::add_device_class (DeviceClass *device_class) diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 6079bc9a7..25bf3d054 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -48,6 +48,100 @@ class DeviceTerminalDefinition; class Netlist; class Net; +/** + * @brief A getter for the ID of an object + */ +template +struct id_attribute +{ + typedef size_t attr_type; + size_t operator() (const T *t) const { return t->id (); } + bool has (const T * /*t*/) const { return true; } +}; + +/** + * @brief A getter for the cluster ID of an object + */ +template +struct cluster_id_attribute +{ + typedef size_t attr_type; + attr_type operator() (const T *t) const { return t->cluster_id (); } + bool has (const T * /*t*/) const { return true; } +}; + +/** + * @brief A getter for the cluster ID of an object + */ +template +struct cell_index_attribute +{ + typedef db::cell_index_type attr_type; + attr_type operator() (const T *t) const { return t->cell_index (); } + bool has (const T * /*t*/) const { return true; } +}; + +/** + * @brief A getter for the name of an object + */ +template +struct name_attribute +{ + typedef std::string attr_type; + const attr_type &operator() (const T *t) const { return t->name (); } + bool has (const T *t) const { return ! t->name ().empty (); } +}; + +/** + * @brief An id-to-object translation table + */ +template +class object_by_attr +{ +public: + typedef typename ATTR::attr_type attr_type; + typedef typename I::value_type value_type; + + object_by_attr (T *self, I (T::*bi) (), I (T::*ei) ()) : mp_self (self), m_bi (bi), m_ei (ei), m_valid (false) + { + // .. nothing yet .. + } + + void invalidate () + { + m_valid = false; + m_map.clear (); + } + + value_type *object_by (const attr_type &attr) const + { + if (! m_valid) { + validate (); + } + typename std::map::const_iterator m = m_map.find (attr); + return m == m_map.end () ? 0 : m->second; + } + +private: + T *mp_self; + I (T::*m_bi) (); + I (T::*m_ei) (); + mutable bool m_valid; + mutable std::map m_map; + + void validate () const + { + ATTR attr; + m_map.clear (); + for (I i = (mp_self->*m_bi) (); i != (mp_self->*m_ei) (); ++i) { + if (attr.has (i.operator-> ())) { + m_map.insert (std::make_pair (attr (i.operator-> ()), i.operator-> ())); + } + } + m_valid = true; + } +}; + /** * @brief A reference to a terminal of a device * @@ -1124,6 +1218,14 @@ public: */ const Pin *pin_by_id (size_t id) const; + /** + * @brief Gets the pin by name + * + * If there is no pin with that name, null is returned. + * NOTE: this is a linear search, so it's performance may not be good for many pins. + */ + const Pin *pin_by_name (const std::string &name) const; + /** * @brief Begin iterator for the pins of the circuit (const version) */ @@ -1184,6 +1286,46 @@ public: return m_nets.end (); } + /** + * @brief Gets the net from a given cluster ID (const version) + * + * If the cluster ID is not valid, null is returned. + */ + const Net *net_by_cluster_id (size_t cluster_id) const + { + return (const_cast (this)->net_by_cluster_id (cluster_id)); + } + + /** + * @brief Gets the net from a given cluster ID (non-const version) + * + * If the cluster ID is not valid, null is returned. + */ + Net *net_by_cluster_id (size_t cluster_id) + { + return m_net_by_cluster_id.object_by (cluster_id); + } + + /** + * @brief Gets the net from a given name (const version) + * + * If the name is not valid, null is returned. + */ + const Net *net_by_name (const std::string &name) const + { + return (const_cast (this)->net_by_name (name)); + } + + /** + * @brief Gets the net from a given name (non-const version) + * + * If the name is not valid, null is returned. + */ + Net *net_by_name (const std::string &name) + { + return m_net_by_name.object_by (name); + } + /** * @brief Adds a device to this circuit * @@ -1207,11 +1349,34 @@ public: } /** - * @brief Gets the device from a given ID (const version) + * @brief Gets the device from a given ID (non-const version) * * If the ID is not valid, null is returned. */ - Device *device_by_id (size_t id); + Device *device_by_id (size_t id) + { + return m_device_by_id.object_by (id); + } + + /** + * @brief Gets the device from a given name (const version) + * + * If the name is not valid, null is returned. + */ + const Device *device_by_name (const std::string &name) const + { + return (const_cast (this)->device_by_name (name)); + } + + /** + * @brief Gets the device from a given name (non-const version) + * + * If the name is not valid, null is returned. + */ + Device *device_by_name (const std::string &name) + { + return m_device_by_name.object_by (name); + } /** * @brief Begin iterator for the devices of the circuit (non-const version) @@ -1268,11 +1433,34 @@ public: } /** - * @brief Gets the subcircuit from a given ID (const version) + * @brief Gets the subcircuit from a given ID (non-const version) * * If the ID is not valid, null is returned. */ - SubCircuit *subcircuit_by_id (size_t id); + SubCircuit *subcircuit_by_id (size_t id) + { + return m_subcircuit_by_id.object_by (id); + } + + /** + * @brief Gets the subcircuit from a given name (const version) + * + * If the name is not valid, null is returned. + */ + const SubCircuit *subcircuit_by_name (const std::string &name) const + { + return (const_cast (this)->subcircuit_by_name (name)); + } + + /** + * @brief Gets the subcircuit from a given name (non-const version) + * + * If the name is not valid, null is returned. + */ + SubCircuit *subcircuit_by_name (const std::string &name) + { + return m_subcircuit_by_name.object_by (name); + } /** * @brief Begin iterator for the subcircuits of the circuit (non-const version) @@ -1350,6 +1538,7 @@ private: friend class Netlist; friend class Net; friend class SubCircuit; + friend class Device; std::string m_name; db::cell_index_type m_cell_index; @@ -1359,10 +1548,12 @@ private: subcircuit_list m_subcircuits; Netlist *mp_netlist; std::vector m_pin_refs; - bool m_valid_device_id_table; - std::map m_device_id_table; - bool m_valid_subcircuit_id_table; - std::map m_subcircuit_id_table; + object_by_attr > m_device_by_id; + object_by_attr > m_subcircuit_by_id; + object_by_attr > m_net_by_cluster_id; + object_by_attr > m_device_by_name; + object_by_attr > m_subcircuit_by_name; + object_by_attr > m_net_by_name; tl::weak_collection m_refs; size_t m_index; @@ -1381,11 +1572,6 @@ private: void set_netlist (Netlist *netlist); bool combine_parallel_devices (const db::DeviceClass &cls); bool combine_serial_devices (const db::DeviceClass &cls); - - void validate_device_id_table (); - void invalidate_device_id_table (); - void validate_subcircuit_id_table (); - void invalidate_subcircuit_id_table (); }; /** @@ -1886,6 +2072,46 @@ public: return m_circuits.end (); } + /** + * @brief Gets the circuit with the given name + * + * If no circuit with that name exists, null is returned. + */ + Circuit *circuit_by_name (const std::string &name) + { + return m_circuit_by_name.object_by (name); + } + + /** + * @brief Gets the circuit with the given name (const version) + * + * If no circuit with that name exists, null is returned. + */ + const Circuit *circuit_by_name (const std::string &name) const + { + return m_circuit_by_name.object_by (name); + } + + /** + * @brief Gets the circuit with the given cell index + * + * If no circuit with that cell index exists, null is returned. + */ + Circuit *circuit_by_cell_index (db::cell_index_type cell_index) + { + return m_circuit_by_cell_index.object_by (cell_index); + } + + /** + * @brief Gets the circuit with the given cell index (const version) + * + * If no circuit with that cell index exists, null is returned. + */ + const Circuit *circuit_by_cell_index (db::cell_index_type cell_index) const + { + return m_circuit_by_cell_index.object_by (cell_index); + } + /** * @brief Gets the top-down circuits iterator (begin) * This iterator will deliver the circuits in a top-down way - i.e. child circuits @@ -2032,6 +2258,8 @@ private: tl::vector > m_child_circuits; tl::vector > m_parent_circuits; size_t m_top_circuits; + object_by_attr > m_circuit_by_name; + object_by_attr > m_circuit_by_cell_index; void invalidate_topology (); void validate_topology (); diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 51e6180dc..b739ce69e 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -651,14 +651,34 @@ Class decl_dbCircuit ("db", "Circuit", "@brief Gets the device object for a given ID.\n" "If the ID is not a valid device ID, nil is returned." ) + + gsi::method ("device_by_name", (db::Device *(db::Circuit::*) (const std::string &)) &db::Circuit::device_by_name, gsi::arg ("name"), + "@brief Gets the device object for a given name.\n" + "If the ID is not a valid device name, nil is returned." + ) + gsi::method ("subcircuit_by_id", (db::SubCircuit *(db::Circuit::*) (size_t)) &db::Circuit::subcircuit_by_id, gsi::arg ("id"), "@brief Gets the subcircuit object for a given ID.\n" "If the ID is not a valid subcircuit ID, nil is returned." ) + + gsi::method ("subcircuit_by_name", (db::SubCircuit *(db::Circuit::*) (const std::string &)) &db::Circuit::subcircuit_by_name, gsi::arg ("name"), + "@brief Gets the subcircuit object for a given name.\n" + "If the ID is not a valid subcircuit name, nil is returned." + ) + + gsi::method ("net_by_cluster_id", (db::Net *(db::Circuit::*) (size_t)) &db::Circuit::net_by_cluster_id, gsi::arg ("cluster_id"), + "@brief Gets the net object corresponding to a specific cluster ID\n" + "If the ID is not a valid pin cluster ID, nil is returned." + ) + + gsi::method ("net_by_name", (db::Net *(db::Circuit::*) (const std::string &)) &db::Circuit::net_by_name, gsi::arg ("name"), + "@brief Gets the net object for a given name.\n" + "If the ID is not a valid net name, nil is returned." + ) + gsi::method ("pin_by_id", &db::Circuit::pin_by_id, gsi::arg ("id"), "@brief Gets the \\Pin object corresponding to a specific ID\n" "If the ID is not a valid pin ID, nil is returned." ) + + gsi::method ("pin_by_name", &db::Circuit::pin_by_name, gsi::arg ("name"), + "@brief Gets the \\Pin object corresponding to a specific name\n" + "If the ID is not a valid pin name, nil is returned." + ) + gsi::method ("pin_count", &db::Circuit::pin_count, "@brief Gets the number of pins in the circuit" ) + @@ -814,6 +834,14 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Removes the given circuit object from the netlist\n" "After the object has been removed, it becomes invalid and cannot be used further." ) + + gsi::method ("circuit_by_cell_index", (db::Circuit *(db::Netlist::*) (db::cell_index_type)) &db::Netlist::circuit_by_cell_index, gsi::arg ("cell_index"), + "@brief Gets the circuit object for a given cell index.\n" + "If the cell index is not valid or no circuit is registered with this index, nil is returned." + ) + + gsi::method ("circuit_by_name", (db::Circuit *(db::Netlist::*) (const std::string &)) &db::Netlist::circuit_by_name, gsi::arg ("name"), + "@brief Gets the circuit object for a given name.\n" + "If the ID is not a valid circuit name, nil is returned." + ) + gsi::iterator ("each_circuit_top_down", (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_top_down, (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_top_down, "@brief Iterates over the circuits top-down\n" "Iterating top-down means the parent circuits come before the child circuits. " diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index a25147743..5a947e847 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -139,6 +139,9 @@ TEST(3_CircuitBasic) EXPECT_EQ (c.pin_by_id (0)->name (), "p1"); EXPECT_EQ (c.pin_by_id (1)->name (), "p2"); EXPECT_EQ (c.pin_by_id (2), 0); + EXPECT_EQ (c.pin_by_name ("p1")->name (), "p1"); + EXPECT_EQ (c.pin_by_name ("doesnt_exist") == 0, true); + EXPECT_EQ (c.pin_by_name ("p2")->name (), "p2"); db::Circuit c2 = c; EXPECT_EQ (c2.name (), "name"); @@ -267,7 +270,7 @@ TEST(4_CircuitDevices) db::Device *dd = new db::Device (&dc1, "dd"); db::Device *d1 = new db::Device (&dc1, "d1"); db::Device *d2a = new db::Device (&dc2, "d2a"); - db::Device *d2b = new db::Device (&dc2, "d2b"); + db::Device *d2b = new db::Device (&dc2, "d2x"); EXPECT_EQ (d1->circuit () == 0, true); @@ -275,19 +278,34 @@ TEST(4_CircuitDevices) EXPECT_EQ (d1->circuit () == c.get (), true); EXPECT_EQ (d1->id (), size_t (1)); EXPECT_EQ (c->device_by_id (d1->id ()) == d1, true); + EXPECT_EQ (c->device_by_name (d1->name ()) == d1, true); + c->add_device (dd); EXPECT_EQ (dd->id (), size_t (2)); EXPECT_EQ (c->device_by_id (dd->id ()) == dd, true); + EXPECT_EQ (c->device_by_name (dd->name ()) == dd, true); + c->add_device (d2a); EXPECT_EQ (d2a->id (), size_t (3)); EXPECT_EQ (c->device_by_id (d2a->id ()) == d2a, true); + EXPECT_EQ (c->device_by_name (d2a->name ()) == d2a, true); + c->add_device (d2b); EXPECT_EQ (d2b->id (), size_t (4)); EXPECT_EQ (c->device_by_id (d2b->id ()) == d2b, true); + EXPECT_EQ (c->device_by_name (d2b->name ()) == d2b, true); + + d2b->set_name ("d2b"); + EXPECT_EQ (c->device_by_id (d2b->id ()) == d2b, true); + EXPECT_EQ (c->device_by_name (d2b->name ()) == d2b, true); + EXPECT_EQ (c->device_by_name ("d2x") == 0, true); + c->remove_device (dd); dd = 0; EXPECT_EQ (c->device_by_id (d2a->id ()) == d2a, true); EXPECT_EQ (c->device_by_id (2) == 0, true); + EXPECT_EQ (c->device_by_name (d2a->name ()) == d2a, true); + EXPECT_EQ (c->device_by_name ("doesnt_exist") == 0, true); EXPECT_EQ (d1->parameter_value (0), 1.0); EXPECT_EQ (d1->parameter_value (1), 2.0); @@ -313,18 +331,33 @@ TEST(4_CircuitDevices) db::Net *n1 = new db::Net (); n1->set_name ("n1"); + n1->set_cluster_id (41); EXPECT_EQ (n1->circuit (), 0); c->add_net (n1); n1->add_terminal (db::NetTerminalRef (d1, 0)); n1->add_terminal (db::NetTerminalRef (d2a, 0)); EXPECT_EQ (n1->circuit (), c.get ()); + EXPECT_EQ (c->net_by_cluster_id (17) == 0, true); + EXPECT_EQ (c->net_by_cluster_id (41) == n1, true); + EXPECT_EQ (c->net_by_name ("doesnt_exist") == 0, true); + EXPECT_EQ (c->net_by_name ("n1") == n1, true); db::Net *n2 = new db::Net (); - n2->set_name ("n2"); + n2->set_name ("n2x"); + n2->set_cluster_id (17); c->add_net (n2); n2->add_terminal (db::NetTerminalRef (d1, 1)); n2->add_terminal (db::NetTerminalRef (d2a, 1)); n2->add_terminal (db::NetTerminalRef (d2b, 0)); + EXPECT_EQ (c->net_by_cluster_id (17) == n2, true); + EXPECT_EQ (c->net_by_name ("n2x") == n2, true); + + n2->set_name ("n2"); + n2->set_cluster_id (42); + EXPECT_EQ (c->net_by_cluster_id (17) == 0, true); + EXPECT_EQ (c->net_by_name ("n2x") == 0, true); + EXPECT_EQ (c->net_by_cluster_id (42) == n2, true); + EXPECT_EQ (c->net_by_name ("n2") == n2, true); EXPECT_EQ (netlist2 (*c), "c:\n" @@ -413,18 +446,33 @@ TEST(4_NetlistSubcircuits) nl->add_device_class (dc); db::Circuit *c1 = new db::Circuit (); + c1->set_cell_index (17); EXPECT_EQ (c1->netlist (), 0); c1->set_name ("c1"); c1->add_pin (db::Pin ("c1p1")); c1->add_pin (db::Pin ("c1p2")); nl->add_circuit (c1); EXPECT_EQ (c1->netlist (), nl.get ()); + EXPECT_EQ (nl->circuit_by_name ("c1") == c1, true); + EXPECT_EQ (nl->circuit_by_name ("doesnt_exist") == 0, true); + EXPECT_EQ (nl->circuit_by_cell_index (17) == c1, true); + EXPECT_EQ (nl->circuit_by_cell_index (42) == 0, true); db::Circuit *c2 = new db::Circuit (); - c2->set_name ("c2"); + c2->set_name ("c2x"); c2->add_pin (db::Pin ("c2p1")); c2->add_pin (db::Pin ("c2p2")); + c2->set_cell_index (41); nl->add_circuit (c2); + EXPECT_EQ (nl->circuit_by_name ("c2x") == c2, true); + EXPECT_EQ (nl->circuit_by_cell_index (41) == c2, true); + + c2->set_name ("c2"); + EXPECT_EQ (nl->circuit_by_name ("c2x") == 0, true); + EXPECT_EQ (nl->circuit_by_name ("c2") == c2, true); + c2->set_cell_index (42); + EXPECT_EQ (nl->circuit_by_cell_index (41) == 0, true); + EXPECT_EQ (nl->circuit_by_cell_index (42) == c2, true); db::Device *d = new db::Device (dc, "D"); c2->add_device (d); @@ -438,13 +486,23 @@ TEST(4_NetlistSubcircuits) EXPECT_EQ (sc1->circuit () == c1, true); EXPECT_EQ (sc1->id (), size_t (1)); EXPECT_EQ (c1->subcircuit_by_id (sc1->id ()) == sc1, true); + EXPECT_EQ (c1->subcircuit_by_id (2) == 0, true); + EXPECT_EQ (c1->subcircuit_by_name (sc1->name ()) == sc1, true); + EXPECT_EQ (c1->subcircuit_by_name ("doesnt_exist") == 0, true); db::SubCircuit *sc2 = new db::SubCircuit (c2); - sc2->set_name ("sc2"); - EXPECT_EQ (refs2string (c2), "sc1,sc2"); + sc2->set_name ("scx"); + EXPECT_EQ (refs2string (c2), "sc1,scx"); c1->add_subcircuit (sc2); EXPECT_EQ (sc2->id (), size_t (2)); EXPECT_EQ (c1->subcircuit_by_id (sc2->id ()) == sc2, true); + EXPECT_EQ (c1->subcircuit_by_name (sc2->name ()) == sc2, true); + + sc2->set_name ("sc2"); + EXPECT_EQ (refs2string (c2), "sc1,sc2"); + EXPECT_EQ (c1->subcircuit_by_id (sc2->id ()) == sc2, true); + EXPECT_EQ (c1->subcircuit_by_name (sc2->name ()) == sc2, true); + EXPECT_EQ (c1->subcircuit_by_name ("scx") == 0, true); db::Net *n2a = new db::Net (); c2->add_net (n2a); diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 9f94ff9ad..9be8cd297 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -37,6 +37,11 @@ class DBNetlist_TestClass < TestBase c.cell_index = 42 assert_equal(c.cell_index, 42) + assert_equal(nl.circuit_by_cell_index(42).name, "XYZ") + assert_equal(nl.circuit_by_name("XYZ").name, "XYZ") + assert_equal(nl.circuit_by_cell_index(17).inspect, "nil") + assert_equal(nl.circuit_by_name("DOESNOTEXIST").inspect, "nil") + cc = RBA::Circuit::new nl.add(cc) cc.name = "ABC" @@ -160,6 +165,9 @@ class DBNetlist_TestClass < TestBase assert_equal(d1.id, 1) assert_equal(c.device_by_id(d1.id).id, 1) assert_equal(c.device_by_id(2).inspect, "nil") + assert_equal(c.device_by_name(d1.name).name, "D1") + assert_equal(c.device_by_id(2).inspect, "nil") + assert_equal(c.device_by_name("doesnt_exist").inspect, "nil") d2 = c.create_device(dc) assert_equal(d2.device_class.id, dc.id) @@ -263,6 +271,9 @@ class DBNetlist_TestClass < TestBase assert_equal(sc1.circuit_ref.name, "CC") assert_equal(c.subcircuit_by_id(sc1.id).id, 1) assert_equal(c.subcircuit_by_id(2).inspect, "nil") + assert_equal(c.subcircuit_by_name(sc1.name).name, "SC1") + assert_equal(c.subcircuit_by_id(2).inspect, "nil") + assert_equal(c.subcircuit_by_name("doesnt_exist").inspect, "nil") refs = [] cc.each_ref { |r| refs << r.name } @@ -460,15 +471,25 @@ class DBNetlist_TestClass < TestBase assert_equal(pinb1.id, 2) assert_equal(pinb2.id, 3) assert_equal(c.pin_by_id(0).name, "A1") + assert_equal(c.pin_by_name("A1").name, "A1") assert_equal(c.pin_by_id(3).name, "B2") + assert_equal(c.pin_by_name("B2").name, "B2") + assert_equal(c.pin_by_id(17).inspect, "nil") + assert_equal(c.pin_by_name("DOESNOTEXIST").inspect, "nil") names = [] c.each_pin { |p| names << p.name } assert_equal(names, [ "A1", "A2", "B1", "B2" ]) net1 = c.create_net + net1.cluster_id = 17 net1.name = "NET1" + assert_equal(c.net_by_cluster_id(17).name, "NET1") + assert_equal(c.net_by_cluster_id(42).inspect, "nil") + assert_equal(c.net_by_name("NET1").name, "NET1") + assert_equal(c.net_by_name("DOESNOTEXIST").inspect, "nil") + net2 = c.create_net net2.name = "NET2" @@ -575,11 +596,13 @@ END c1 = RBA::Circuit::new c1.name = "C1" + c1.cell_index = 17 nl.add(c1) assert_equal(nl.top_circuit_count, 1) c2 = RBA::Circuit::new c2.name = "C2" + c1.cell_index = 42 nl.add(c2) assert_equal(nl.top_circuit_count, 2) From a787204e77aef1d3cfa290e7637fb90b2570fa06 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Dec 2018 13:28:11 +0100 Subject: [PATCH 148/335] WIP: connect and disconnect terminal by name in GSI --- src/db/db/gsiDeclDbNetlist.cc | 26 ++++++++++++++++++++++++-- testdata/ruby/dbNetlist.rb | 19 ++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index b739ce69e..33d98a3d9 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -42,11 +42,25 @@ Class decl_dbPin ("db", "Pin", "This class has been added in version 0.26." ); +static void device_connect_terminal_by_name (db::Device *device, const std::string &terminal_name, db::Net *net) +{ + if (! device->device_class ()) { + throw tl::Exception (tl::to_string (tr ("Device does not have a device class"))); + } + size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name); + device->connect_terminal (terminal_id, net); +} + static void device_disconnect_terminal (db::Device *device, size_t terminal_id) { device->connect_terminal (terminal_id, 0); } +static void device_disconnect_terminal_by_name (db::Device *device, const std::string &terminal_name) +{ + device_connect_terminal_by_name (device, terminal_name, 0); +} + Class decl_dbDevice ("db", "Device", gsi::method ("device_class", &db::Device::device_class, "@brief Gets the device class the device belongs to.\n" @@ -78,6 +92,14 @@ Class decl_dbDevice ("db", "Device", gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal, gsi::arg ("terminal_id"), "@brief Disconnects the given terminal from any net.\n" ) + + gsi::method_ext ("connect_terminal", &device_connect_terminal_by_name, gsi::arg ("terminal_name"), gsi::arg ("net"), + "@brief Connects the given terminal to the specified net.\n" + "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised." + ) + + gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal_by_name, gsi::arg ("terminal_name"), + "@brief Disconnects the given terminal from any net.\n" + "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised." + ) + gsi::method ("parameter", (double (db::Device::*) (size_t) const) &db::Device::parameter_value, gsi::arg ("param_id"), "@brief Gets the parameter value for the given parameter ID." ) + @@ -412,7 +434,7 @@ Class decl_dbDeviceClass ("db", "DeviceClass", "Parameter definition IDs are used in some places to reference a specific parameter of a device. " "This method obtains the corresponding definition object." ) + - gsi::method ("has_parameter", &db::DeviceClass::has_parameter_with_name, gsi::arg ("name"), + gsi::method ("has_parameter?", &db::DeviceClass::has_parameter_with_name, gsi::arg ("name"), "@brief Returns true, if the device class has a parameter with the given name.\n" ) + gsi::method ("parameter_id", &db::DeviceClass::parameter_id_for_name, gsi::arg ("name"), @@ -420,7 +442,7 @@ Class decl_dbDeviceClass ("db", "DeviceClass", "An exception is thrown if there is no parameter with the given name. Use \\has_parameter to check " "whether the name is a valid parameter name." ) + - gsi::method ("has_terminal", &db::DeviceClass::has_terminal_with_name, gsi::arg ("name"), + gsi::method ("has_terminal?", &db::DeviceClass::has_terminal_with_name, gsi::arg ("name"), "@brief Returns true, if the device class has a terminal with the given name.\n" ) + gsi::method ("terminal_id", &db::DeviceClass::terminal_id_for_name, gsi::arg ("name"), diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 9be8cd297..214983856 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -156,6 +156,11 @@ class DBNetlist_TestClass < TestBase pd.description = "Terminal B" dc.add_terminal(pd) + assert_equal(dc.has_terminal?("X"), false) + assert_equal(dc.has_terminal?("A"), true) + assert_equal(dc.terminal_id("A"), 0) + assert_equal(dc.terminal_id("B"), 1) + c = RBA::Circuit::new nl.add(c) @@ -221,6 +226,14 @@ class DBNetlist_TestClass < TestBase assert_equal(d1.net_for_terminal(1).name, "NET") assert_equal(d1.net_for_terminal(0).inspect, "nil") + d1.disconnect_terminal("B") + assert_equal(net.terminal_count, 0) + assert_equal(d1.net_for_terminal(1).inspect, "nil") + + d1.connect_terminal("B", net) + assert_equal(net.terminal_count, 1) + assert_equal(d1.net_for_terminal(1).name, "NET") + d2.connect_terminal(0, net) assert_equal(net.terminal_count, 2) @@ -547,9 +560,9 @@ class DBNetlist_TestClass < TestBase dc.add_parameter(RBA::DeviceParameterDefinition::new("U", "Parameter U", 1.0)) dc.add_parameter(RBA::DeviceParameterDefinition::new("V", "Parameter V", 2.0)) - assert_equal(dc.has_parameter("U"), true) - assert_equal(dc.has_parameter("V"), true) - assert_equal(dc.has_parameter("X"), false) + assert_equal(dc.has_parameter?("U"), true) + assert_equal(dc.has_parameter?("V"), true) + assert_equal(dc.has_parameter?("X"), false) assert_equal(dc.parameter_id("U"), 0) assert_equal(dc.parameter_id("V"), 1) error = false From c841b84867aea193819164a661dad728715b45bd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Dec 2018 13:37:52 +0100 Subject: [PATCH 149/335] WIP: some minor refactoring. --- src/db/db/dbNetlist.cc | 11 +- src/db/db/dbNetlist.h | 3 +- src/db/db/dbNetlistExtractor.cc | 4 +- src/db/db/gsiDeclDbNetlist.cc | 7 +- .../unit_tests/dbNetlistDeviceClassesTests.cc | 140 +++++++++--------- src/db/unit_tests/dbNetlistTests.cc | 26 ++-- 6 files changed, 89 insertions(+), 102 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index d301a75b9..1f740ef0f 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -559,9 +559,7 @@ Circuit &Circuit::operator= (const Circuit &other) m_subcircuit_by_name.invalidate (); m_net_by_name.invalidate (); - for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { - add_pin (*i); - } + m_pins = other.m_pins; std::map device_table; for (const_device_iterator i = other.begin_devices (); i != other.end_devices (); ++i) { @@ -711,9 +709,9 @@ Circuit::const_child_circuit_iterator Circuit::end_parents () const return reinterpret_cast &> (mp_netlist->parent_circuits (const_cast (this))).end (); } -const Pin &Circuit::add_pin (const Pin &pin) +const Pin &Circuit::add_pin (const std::string &name) { - m_pins.push_back (pin); + m_pins.push_back (Pin (name)); m_pins.back ().set_id (m_pins.size () - 1); return m_pins.back (); } @@ -1503,8 +1501,7 @@ void Netlist::make_top_level_pins () // create pins for the named nets and connect them for (Circuit::net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { if (! n->name ().empty () && n->terminal_count () + n->pin_count () > 0) { - Pin pin (n->name ()); - pin = circuit->add_pin (pin); + Pin pin = circuit->add_pin (n->name ()); circuit->connect_pin (pin.id (), n.operator-> ()); } } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 25bf3d054..b1b627ffc 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -640,7 +640,6 @@ public: private: friend class Circuit; - tl::weak_ptr m_circuit; std::string m_name; size_t m_id; @@ -1187,7 +1186,7 @@ public: * @brief Adds a pin to this circuit * The circuit takes over ownership of the object. */ - const Pin &add_pin(const Pin &pin); + const Pin &add_pin(const std::string &name); /** * @brief Begin iterator for the pins of the circuit (non-const version) diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 91650e852..6dfd810eb 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -171,9 +171,7 @@ void NetlistExtractor::make_and_connect_subcircuits (db::Circuit *circuit, size_t NetlistExtractor::make_pin (db::Circuit *circuit, db::Net *net) { - db::Pin pin (net->name ()); - - size_t pin_id = circuit->add_pin (pin).id (); + size_t pin_id = circuit->add_pin (net->name ()).id (); net->add_pin (db::NetPinRef (pin_id)); circuit->connect_pin (pin_id, net); diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 33d98a3d9..194ad572e 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -597,11 +597,6 @@ Class decl_GenericDeviceClass (decl_dbDeviceClass, "db", "Ge "This class has been added in version 0.26." ); -static const db::Pin &create_pin (db::Circuit *c, const std::string &name) -{ - return c->add_pin (db::Pin (name)); -} - static db::Net *create_net (db::Circuit *c, const std::string &name) { db::Net *n = new db::Net (); @@ -649,7 +644,7 @@ static void circuit_disconnect_pin1 (db::Circuit *c, const db::Pin *pin) } Class decl_dbCircuit ("db", "Circuit", - gsi::method_ext ("create_pin", &gsi::create_pin, gsi::arg ("name"), + gsi::method ("create_pin", &db::Circuit::add_pin, gsi::arg ("name"), "@brief Creates a new \\Pin object inside the circuit\n" "This object will describe a pin of the circuit. A circuit connects " "to the outside through such a pin. The pin is added after all existing " diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc index 2864e8cfe..510f85ee6 100644 --- a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -43,8 +43,8 @@ TEST(1_SerialResistors) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (r1); circuit->add_device (r2); @@ -94,8 +94,8 @@ TEST(2_SerialResistors1Swapped) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (r1); circuit->add_device (r2); @@ -145,8 +145,8 @@ TEST(3_SerialResistors1OtherSwapped) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (r1); circuit->add_device (r2); @@ -196,8 +196,8 @@ TEST(4_SerialResistors2Swapped) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (r1); circuit->add_device (r2); @@ -247,9 +247,9 @@ TEST(5_SerialResistorsNoCombination) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); - db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); circuit->add_device (r1); circuit->add_device (r2); @@ -301,8 +301,8 @@ TEST(6_ParallelResistors) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (r1); circuit->add_device (r2); @@ -349,8 +349,8 @@ TEST(7_ParallelResistors1Swapped) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (r1); circuit->add_device (r2); @@ -397,8 +397,8 @@ TEST(8_ParallelResistors1OtherSwapped) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (r1); circuit->add_device (r2); @@ -445,8 +445,8 @@ TEST(9_ParallelResistors2Swapped) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (r1); circuit->add_device (r2); @@ -506,8 +506,8 @@ TEST(10_ComplexRegistorCombination) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (r1); circuit->add_device (r2); @@ -568,8 +568,8 @@ TEST(11_SerialInductors) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (l1); circuit->add_device (l2); @@ -619,8 +619,8 @@ TEST(12_ParallelInductors) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (l1); circuit->add_device (l2); @@ -667,8 +667,8 @@ TEST(13_SerialCapacitors) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (c1); circuit->add_device (c2); @@ -718,8 +718,8 @@ TEST(14_ParallelCapacitors) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (c1); circuit->add_device (c2); @@ -766,8 +766,8 @@ TEST(15_SerialDiodes) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (d1); circuit->add_device (d2); @@ -820,8 +820,8 @@ TEST(16_ParallelDiodes) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (d1); circuit->add_device (d2); @@ -868,8 +868,8 @@ TEST(17_AntiParallelDiodes) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); circuit->add_device (d1); circuit->add_device (d2); @@ -925,9 +925,9 @@ TEST(20_ParallelMOS3Transistors) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); - db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); circuit->add_device (d1); circuit->add_device (d2); @@ -986,9 +986,9 @@ TEST(21_AntiParallelMOS3Transistors) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); - db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); circuit->add_device (d1); circuit->add_device (d2); @@ -1047,10 +1047,10 @@ TEST(22_ParallelMOS3TransistorsDisconnectedGates) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); - db::Pin pin_c1 = circuit->add_pin (db::Pin ("C1")); - db::Pin pin_c2 = circuit->add_pin (db::Pin ("C2")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c1 = circuit->add_pin ("C1"); + db::Pin pin_c2 = circuit->add_pin ("C2"); circuit->add_device (d1); circuit->add_device (d2); @@ -1116,9 +1116,9 @@ TEST(23_ParallelMOS3TransistorsDifferentLength) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); - db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); circuit->add_device (d1); circuit->add_device (d2); @@ -1180,10 +1180,10 @@ TEST(30_ParallelMOS4Transistors) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); - db::Pin pin_c = circuit->add_pin (db::Pin ("C")); - db::Pin pin_d = circuit->add_pin (db::Pin ("D")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + db::Pin pin_d = circuit->add_pin ("D"); circuit->add_device (d1); circuit->add_device (d2); @@ -1248,10 +1248,10 @@ TEST(31_AntiParallelMOS4Transistors) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); - db::Pin pin_c = circuit->add_pin (db::Pin ("C")); - db::Pin pin_d = circuit->add_pin (db::Pin ("D")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + db::Pin pin_d = circuit->add_pin ("D"); circuit->add_device (d1); circuit->add_device (d2); @@ -1316,11 +1316,11 @@ TEST(32_ParallelMOS4TransistorsDisconnectedGates) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); - db::Pin pin_c1 = circuit->add_pin (db::Pin ("C1")); - db::Pin pin_c2 = circuit->add_pin (db::Pin ("C2")); - db::Pin pin_d = circuit->add_pin (db::Pin ("D")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c1 = circuit->add_pin ("C1"); + db::Pin pin_c2 = circuit->add_pin ("C2"); + db::Pin pin_d = circuit->add_pin ("D"); circuit->add_device (d1); circuit->add_device (d2); @@ -1392,11 +1392,11 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); - db::Pin pin_c = circuit->add_pin (db::Pin ("C")); - db::Pin pin_d1 = circuit->add_pin (db::Pin ("D1")); - db::Pin pin_d2 = circuit->add_pin (db::Pin ("D2")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + db::Pin pin_d1 = circuit->add_pin ("D1"); + db::Pin pin_d2 = circuit->add_pin ("D2"); circuit->add_device (d1); circuit->add_device (d2); @@ -1468,10 +1468,10 @@ TEST(34_ParallelMOS4TransistorsDifferentLength) db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); - db::Pin pin_a = circuit->add_pin (db::Pin ("A")); - db::Pin pin_b = circuit->add_pin (db::Pin ("B")); - db::Pin pin_c = circuit->add_pin (db::Pin ("C")); - db::Pin pin_d = circuit->add_pin (db::Pin ("D")); + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + db::Pin pin_d = circuit->add_pin ("D"); circuit->add_device (d1); circuit->add_device (d2); diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 5a947e847..ad9ab0407 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -130,10 +130,8 @@ TEST(3_CircuitBasic) c.set_name ("name"); EXPECT_EQ (c.name (), "name"); - db::Pin p1 ("p1"); - db::Pin p2 ("p2"); - c.add_pin (p1); - c.add_pin (p2); + db::Pin p1 = c.add_pin ("p1"); + db::Pin p2 = c.add_pin ("p2"); EXPECT_EQ (pins2string (c), "p1#0,p2#1"); EXPECT_EQ (c.pin_by_id (0)->name (), "p1"); @@ -449,8 +447,8 @@ TEST(4_NetlistSubcircuits) c1->set_cell_index (17); EXPECT_EQ (c1->netlist (), 0); c1->set_name ("c1"); - c1->add_pin (db::Pin ("c1p1")); - c1->add_pin (db::Pin ("c1p2")); + c1->add_pin ("c1p1"); + c1->add_pin ("c1p2"); nl->add_circuit (c1); EXPECT_EQ (c1->netlist (), nl.get ()); EXPECT_EQ (nl->circuit_by_name ("c1") == c1, true); @@ -460,8 +458,8 @@ TEST(4_NetlistSubcircuits) db::Circuit *c2 = new db::Circuit (); c2->set_name ("c2x"); - c2->add_pin (db::Pin ("c2p1")); - c2->add_pin (db::Pin ("c2p2")); + c2->add_pin ("c2p1"); + c2->add_pin ("c2p2"); c2->set_cell_index (41); nl->add_circuit (c2); EXPECT_EQ (nl->circuit_by_name ("c2x") == c2, true); @@ -726,18 +724,18 @@ TEST(8_NetSubCircuitsEditing) { db::Circuit c; c.set_name ("c"); - c.add_pin (db::Pin ("X")); - c.add_pin (db::Pin ("Y")); + c.add_pin ("X"); + c.add_pin ("Y"); db::Circuit cc1; cc1.set_name ("sc1"); - cc1.add_pin (db::Pin ("A")); - cc1.add_pin (db::Pin ("B")); + cc1.add_pin ("A"); + cc1.add_pin ("B"); db::Circuit cc2; cc2.set_name ("sc2"); - cc2.add_pin (db::Pin ("A")); - cc2.add_pin (db::Pin ("B")); + cc2.add_pin ("A"); + cc2.add_pin ("B"); db::SubCircuit *sc1 = new db::SubCircuit (&cc1, "sc1"); c.add_subcircuit (sc1); From ec7ab8e01d32956735a1322d635346af9289a0e0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Dec 2018 14:15:58 +0100 Subject: [PATCH 150/335] WIP: moved code. --- src/db/db/gsiDeclDbNetlistDeviceClasses.cc | 42 --------------------- src/gsi/gsi/gsiMethods.h | 43 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/src/db/db/gsiDeclDbNetlistDeviceClasses.cc b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc index 8ce4a554c..e78bfd3bd 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceClasses.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc @@ -26,48 +26,6 @@ namespace gsi { -// TODO: move this to gsiMethods.h - -template -class ConstantValueGetter - : public StaticMethodBase -{ -public: - ConstantValueGetter (const std::string &name, const R &v, const std::string &doc) - : StaticMethodBase (name, doc, true), m_v (v) - { - } - - void initialize () - { - this->clear (); - // Note: a constant must not return a reference to an existing object, hence "set_return_new": - this->template set_return_new (); - } - - virtual MethodBase *clone () const - { - return new ConstantValueGetter (*this); - } - - virtual void call (void *, SerialArgs &, SerialArgs &ret) const - { - mark_called (); - ret.write (m_v); - } - -private: - R m_v; -}; - -template -Methods -constant (const std::string &name, const R &v, const std::string &doc = std::string ()) -{ - return Methods (new ConstantValueGetter (name, v, doc)); -} - - extern Class decl_dbDeviceClass; Class decl_dbDeviceClassResistor (decl_dbDeviceClass, "db", "DeviceClassResistor", diff --git a/src/gsi/gsi/gsiMethods.h b/src/gsi/gsi/gsiMethods.h index 8b1cc2ef5..d280395c1 100644 --- a/src/gsi/gsi/gsiMethods.h +++ b/src/gsi/gsi/gsiMethods.h @@ -810,6 +810,49 @@ constant (const std::string &name, R (*m) (), const std::string &doc = std::stri return Methods (new ConstantGetter (name, m, doc)); } +/** + * @brief A helper class to create a constant (a static method with "const" attribute, not taking any arguments) + * This version creates a constant getter from a real constant value. + */ +template +class ConstantValueGetter + : public StaticMethodBase +{ +public: + ConstantValueGetter (const std::string &name, const R &v, const std::string &doc) + : StaticMethodBase (name, doc, true), m_v (v) + { + } + + void initialize () + { + this->clear (); + // Note: a constant must not return a reference to an existing object, hence "set_return_new": + this->template set_return_new (); + } + + virtual MethodBase *clone () const + { + return new ConstantValueGetter (*this); + } + + virtual void call (void *, SerialArgs &, SerialArgs &ret) const + { + mark_called (); + ret.write (m_v); + } + +private: + R m_v; +}; + +template +Methods +constant (const std::string &name, const R &v, const std::string &doc = std::string ()) +{ + return Methods (new ConstantValueGetter (name, v, doc)); +} + struct return_by_value { typedef tl::False is_factory; From b512f628bcc5cb8739e859f537fd89b9ebed6a92 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Dec 2018 14:54:59 +0100 Subject: [PATCH 151/335] WIP: specific device extractor and GSI binding. --- src/db/db/db.pro | 6 +- src/db/db/dbNetlistDeviceExtractorClasses.cc | 128 +++++++++++++++++++ src/db/db/dbNetlistDeviceExtractorClasses.h | 81 ++++++++++++ src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 57 ++++++++- src/db/unit_tests/dbNetlistExtractorTests.cc | 95 +------------- 5 files changed, 268 insertions(+), 99 deletions(-) create mode 100644 src/db/db/dbNetlistDeviceExtractorClasses.cc create mode 100644 src/db/db/dbNetlistDeviceExtractorClasses.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index dde3156b3..ba81bd02b 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -150,7 +150,8 @@ SOURCES = \ dbNetlistExtractor.cc \ gsiDeclDbNetlistDeviceClasses.cc \ gsiDeclDbNetlistDeviceExtractor.cc \ - gsiDeclDbHierNetworkProcessor.cc + gsiDeclDbHierNetworkProcessor.cc \ + dbNetlistDeviceExtractorClasses.cc HEADERS = \ dbArray.h \ @@ -264,7 +265,8 @@ HEADERS = \ dbNetlist.h \ dbNetlistDeviceClasses.h \ dbNetlistDeviceExtractor.h \ - dbNetlistExtractor.h + dbNetlistExtractor.h \ + dbNetlistDeviceExtractorClasses.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc new file mode 100644 index 000000000..0eb21f6cc --- /dev/null +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -0,0 +1,128 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlistDeviceExtractorClasses.h" +#include "dbNetlistDeviceClasses.h" + +// --------------------------------------------------------------------------------- +// NetlistDeviceExtractorMOS3Transistor implementation + +namespace db +{ + +NetlistDeviceExtractorMOS3Transistor::NetlistDeviceExtractorMOS3Transistor (const std::string &name) + : db::NetlistDeviceExtractor (name) +{ + // .. nothing yet .. +} + +void NetlistDeviceExtractorMOS3Transistor::setup () +{ + define_layer ("SD", "Source/drain diffusion"); + define_layer ("G", "Gate"); + define_layer ("P", "Poly"); + + register_device_class (new db::DeviceClassMOS3Transistor ()); +} + +db::Connectivity NetlistDeviceExtractorMOS3Transistor::get_connectivity (const db::Layout & /*layout*/, const std::vector &layers) const +{ + tl_assert (layers.size () == 3); + + unsigned int diff = layers [0]; + unsigned int gate = layers [1]; + // not used for device recognition: poly (2), but used for producing the gate terminals + + // The layer definition is diff, gate + db::Connectivity conn; + // collect all connected diffusion shapes + conn.connect (diff, diff); + // collect all connected gate shapes + conn.connect (gate, gate); + // connect gate with diff to detect gate/diffusion boundary + conn.connect (diff, gate); + return conn; +} + +void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector &layer_geometry) +{ + const db::Region &rdiff = layer_geometry [0]; + const db::Region &rgates = layer_geometry [1]; + + for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) { + + db::Region rgate (*p); + db::Region rdiff2gate = rdiff.selected_interacting (rgate); + + if (rdiff2gate.empty ()) { + error (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p); + } else { + + unsigned int terminal_geometry_index = 0; + unsigned int gate_geometry_index = 2; + + if (rdiff2gate.size () != 2) { + error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.size ())), *p); + continue; + } + + db::Edges edges (rgate.edges () & rdiff2gate.edges ()); + if (edges.size () != 2) { + error (tl::sprintf (tl::to_string (tr ("Expected two edges interacting gate/diff (found %d) - width and length may be incorrect")), int (edges.size ())), *p); + continue; + } + + if (! p->is_box ()) { + error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p); + } + + db::Device *device = create_device (); + + device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, dbu () * edges.length () * 0.5); + device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, dbu () * (p->perimeter () - edges.length ()) * 0.5); + + int diff_index = 0; + for (db::Region::const_iterator d = rdiff2gate.begin (); !d.at_end () && diff_index < 2; ++d, ++diff_index) { + + // count the number of gate shapes attached to this shape and distribute the area of the + // diffusion region to the number of gates + int n = rgates.selected_interacting (db::Region (*d)).size (); + tl_assert (n > 0); + + device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, dbu () * dbu () * d->area () / double (n)); + + define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, terminal_geometry_index, *d); + + } + + define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_geometry_index, *p); + + // output the device for debugging + device_out (device, rdiff2gate, rgate); + + } + + } +} + +} diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.h b/src/db/db/dbNetlistDeviceExtractorClasses.h new file mode 100644 index 000000000..7d8e9e09f --- /dev/null +++ b/src/db/db/dbNetlistDeviceExtractorClasses.h @@ -0,0 +1,81 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbNetlistDeviceExtractorClasses +#define _HDR_dbNetlistDeviceExtractorClasses + +#include "dbNetlistDeviceExtractor.h" + +namespace db +{ + +/** + * @brief A device extractor for a three-terminal MOS transistor + * + * This class supplies the generic extractor for a MOS device. + * The device is defined by two basic input layers: the diffusion area + * (source and drain) and the gate area. It requires a third layer + * (poly) to put the gate terminals on. The separation between poly + * and allows separating the device recognition layer (gate) from the + * conductive layer. + * + * The device class produced by this extractor is DeviceClassMOS3Transistor. + * The extractor extracts the four parameters of this class: L, W, AS and AD. + * + * The diffusion area is distributed on the number of gates connecting to + * the particular source or drain area. + */ +class DB_PUBLIC NetlistDeviceExtractorMOS3Transistor + : public db::NetlistDeviceExtractor +{ +public: + NetlistDeviceExtractorMOS3Transistor (const std::string &name); + + virtual void setup (); + virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector &layers) const; + virtual void extract_devices (const std::vector &layer_geometry); + +protected: + /** + * @brief A cappback when the device is produced + * This callback is provided as a debugging port + */ + virtual void device_out (const db::Device * /*device*/, const db::Region & /*diff*/, const db::Region & /*gate*/) + { + // .. no specific implementation .. + } +}; + +} + +namespace tl +{ + +template<> struct tl::type_traits : public tl::type_traits +{ + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + +#endif diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index 87fe8df86..f56ffcb94 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -22,6 +22,7 @@ #include "gsiDecl.h" #include "dbNetlistDeviceExtractor.h" +#include "dbNetlistDeviceExtractorClasses.h" namespace { @@ -212,16 +213,32 @@ Class decl_dbNetlistDeviceExtractorLa "This class has been introduced in version 0.26." ); -Class decl_dbNetlistDeviceExtractor ("db", "NetlistDeviceExtractorImpl", - gsi::method ("name", &NetlistDeviceExtractorImpl::name, +Class decl_dbNetlistDeviceExtractor ("db", "DeviceExtractorBase", + gsi::method ("name", &db::NetlistDeviceExtractor::name, "@brief Gets the name of the device extractor and the device class." ) + + gsi::iterator ("each_layer_definition", &db::NetlistDeviceExtractor::begin_layer_definitions, &db::NetlistDeviceExtractor::end_layer_definitions, + "@brief Iterates over all layer definitions." + ) + + gsi::iterator ("each_error", &db::NetlistDeviceExtractor::begin_errors, &db::NetlistDeviceExtractor::end_errors, + "@brief Iterates over all errors collected in the device extractor." + ), + "@brief The base class for all device extractors.\n" + "This is an abstract base class for device extractors. See \\NetlistDeviceExtractor for a generic " + "class which you can reimplement to supply your own customized device extractor. " + "In many cases using one of the preconfigured specific device extractors may be useful already and " + "it's not required to implement a custom one. For an example about a preconfigured device extractor see " + "\\DeviceExtractorMOS3Transistor.\n" + "\n" + "This class cannot and should not be instantiated explicitly. Use one of the subclasses instead.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_NetlistDeviceExtractorImpl (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractor", gsi::method ("name=", &NetlistDeviceExtractorImpl::set_name, "@brief Sets the name of the device extractor and the device class." ) + - gsi::iterator ("each_layer_definition", &NetlistDeviceExtractorImpl::begin_layer_definitions, &NetlistDeviceExtractorImpl::end_layer_definitions, - "@brief Iterates over all layer definitions." - ) + gsi::callback ("setup", &NetlistDeviceExtractorImpl::setup, &NetlistDeviceExtractorImpl::cb_setup, "@brief Sets up the extractor.\n" "This method is supposed to set up the device extractor. This involves three basic steps:\n" @@ -368,4 +385,34 @@ Class decl_dbNetlistDeviceExtractor ("db", "NetlistD "This class has been introduced in version 0.26." ); +db::NetlistDeviceExtractorMOS3Transistor *make_mos3_extractor (const std::string &name) +{ + return new db::NetlistDeviceExtractorMOS3Transistor (name); +} + +Class decl_NetlistDeviceExtractorMOS3Transistor (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractorMOS3Transistor", + gsi::constructor ("new", &make_mos3_extractor, gsi::arg ("name"), + "@brief Creates a new device extractor with the given name." + ), + "@brief A device extractor for a three-terminal MOS transistor\n" + "\n" + "This class supplies the generic extractor for a MOS device.\n" + "The device is defined by two basic input layers: the diffusion area\n" + "(source and drain) and the gate area. It requires a third layer\n" + "(poly) to put the gate terminals on. The separation between poly\n" + "and allows separating the device recognition layer (gate) from the\n" + "conductive layer.\n" + "\n" + "The device class produced by this extractor is \\DeviceClassMOS3Transistor.\n" + "The extractor extracts the four parameters of this class: L, W, AS and AD.\n" + "\n" + "The diffusion area is distributed on the number of gates connecting to\n" + "the particular source or drain area.\n" + "\n" + "This class is a closed one and methods cannot be reimplemented. To reimplement " + "specific methods, see \\DeviceExtractor.\n" + "\n" + "This class has been introduced in version 0.26." +); + } diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 3ee586167..2f50910af 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -21,7 +21,7 @@ */ -#include "dbNetlistDeviceExtractor.h" +#include "dbNetlistDeviceExtractorClasses.h" #include "dbNetlistExtractor.h" #include "dbNetlistDeviceClasses.h" #include "dbLayout.h" @@ -59,11 +59,11 @@ static std::string device_name (const db::Device &device) } class MOSFETExtractor - : public db::NetlistDeviceExtractor + : public db::NetlistDeviceExtractorMOS3Transistor { public: MOSFETExtractor (const std::string &name, db::Layout *debug_out) - : db::NetlistDeviceExtractor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) + : db::NetlistDeviceExtractorMOS3Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) { if (mp_debug_out) { m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); @@ -71,95 +71,6 @@ public: } } - virtual void setup () - { - define_layer ("SD", "Source/drain diffusion"); - define_layer ("G", "Gate"); - define_layer ("P", "Poly"); - - register_device_class (new db::DeviceClassMOS3Transistor ()); - } - - virtual db::Connectivity get_connectivity (const db::Layout & /*layout*/, const std::vector &layers) const - { - tl_assert (layers.size () == 3); - - unsigned int diff = layers [0]; - unsigned int gate = layers [1]; - // not used for device recognition: poly (2), but used for producing the gate terminals - - // The layer definition is diff, gate - db::Connectivity conn; - // collect all connected diffusion shapes - conn.connect (diff, diff); - // collect all connected gate shapes - conn.connect (gate, gate); - // connect gate with diff to detect gate/diffusion boundary - conn.connect (diff, gate); - return conn; - } - - virtual void extract_devices (const std::vector &layer_geometry) - { - const db::Region &rdiff = layer_geometry [0]; - const db::Region &rgates = layer_geometry [1]; - - for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) { - - db::Region rgate (*p); - db::Region rdiff2gate = rdiff.selected_interacting (rgate); - - if (rdiff2gate.empty ()) { - error (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p); - } else { - - unsigned int terminal_geometry_index = 0; - unsigned int gate_geometry_index = 2; - - if (rdiff2gate.size () != 2) { - error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.size ())), *p); - continue; - } - - db::Edges edges (rgate.edges () & rdiff2gate.edges ()); - if (edges.size () != 2) { - error (tl::sprintf (tl::to_string (tr ("Expected two edges interacting gate/diff (found %d) - width and length may be incorrect")), int (edges.size ())), *p); - continue; - } - - if (! p->is_box ()) { - error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p); - } - - db::Device *device = create_device (); - - device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, dbu () * edges.length () * 0.5); - device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, dbu () * (p->perimeter () - edges.length ()) * 0.5); - - int diff_index = 0; - for (db::Region::const_iterator d = rdiff2gate.begin (); !d.at_end () && diff_index < 2; ++d, ++diff_index) { - - // count the number of gate shapes attached to this shape and distribute the area of the - // diffusion region to the number of gates - int n = rgates.selected_interacting (db::Region (*d)).size (); - tl_assert (n > 0); - - device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, dbu () * dbu () * d->area () / double (n)); - - define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, terminal_geometry_index, *d); - - } - - define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_geometry_index, *p); - - // output the device for debugging - device_out (device, rdiff2gate, rgate); - - } - - } - } - private: db::Layout *mp_debug_out; unsigned int m_ldiff, m_lgate; From 16a2b1982d71d7be8c854ceda0d9b396e613d75b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Dec 2018 17:53:46 +0100 Subject: [PATCH 152/335] WIP: added one more level of abstraction to layout-to-netlist extraction (db::LayoutToNetlist) for easier use. --- src/db/db/db.pro | 6 +- src/db/db/dbDeepShapeStore.cc | 28 ++ src/db/db/dbDeepShapeStore.h | 10 + src/db/db/dbLayoutToNetlist.cc | 291 +++++++++++++++ src/db/db/dbLayoutToNetlist.h | 260 +++++++++++++ src/db/db/dbNetlistDeviceExtractor.h | 2 +- src/db/db/dbRecursiveShapeIterator.cc | 22 ++ src/db/db/dbRecursiveShapeIterator.h | 14 + src/db/unit_tests/dbLayoutToNetlistTests.cc | 351 ++++++++++++++++++ src/db/unit_tests/dbNetlistExtractorTests.cc | 99 +---- .../dbRecursiveShapeIteratorTests.cc | 14 + src/db/unit_tests/unit_tests.pro | 3 +- 12 files changed, 1005 insertions(+), 95 deletions(-) create mode 100644 src/db/db/dbLayoutToNetlist.cc create mode 100644 src/db/db/dbLayoutToNetlist.h create mode 100644 src/db/unit_tests/dbLayoutToNetlistTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index ba81bd02b..13ac344f1 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -151,7 +151,8 @@ SOURCES = \ gsiDeclDbNetlistDeviceClasses.cc \ gsiDeclDbNetlistDeviceExtractor.cc \ gsiDeclDbHierNetworkProcessor.cc \ - dbNetlistDeviceExtractorClasses.cc + dbNetlistDeviceExtractorClasses.cc \ + dbLayoutToNetlist.cc HEADERS = \ dbArray.h \ @@ -266,7 +267,8 @@ HEADERS = \ dbNetlistDeviceClasses.h \ dbNetlistDeviceExtractor.h \ dbNetlistExtractor.h \ - dbNetlistDeviceExtractorClasses.h + dbNetlistDeviceExtractorClasses.h \ + dbLayoutToNetlist.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index c0e900a17..1ca9af602 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -113,6 +113,34 @@ DeepLayer::insert_into (db::Layout *into_layout, db::cell_index_type into_cell, const_cast (mp_store.get ())->insert (*this, into_layout, into_cell, into_layer); } +bool DeepLayer::operator< (const DeepLayer &other) const +{ + if (mp_store.get () != other.mp_store.get ()) { + return mp_store.get () < other.mp_store.get (); + } + if (m_layout != other.m_layout) { + return m_layout < other.m_layout; + } + if (m_layer != other.m_layer) { + return m_layer < other.m_layer; + } + return false; +} + +bool DeepLayer::operator== (const DeepLayer &other) const +{ + if (mp_store.get () != other.mp_store.get ()) { + return false; + } + if (m_layout != other.m_layout) { + return false; + } + if (m_layer != other.m_layer) { + return false; + } + return true; +} + db::Layout & DeepLayer::layout () { diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 7fcc7ec07..7dac3f9d1 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -77,6 +77,16 @@ public: */ DeepLayer &operator= (const DeepLayer &other); + /** + * @brief Less operator + */ + bool operator< (const DeepLayer &other) const; + + /** + * @brief Equality operator + */ + bool operator== (const DeepLayer &other) const; + /** * @brief Gets the layout object * The return value is guaranteed to be non-null. diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc new file mode 100644 index 000000000..d0437d16d --- /dev/null +++ b/src/db/db/dbLayoutToNetlist.cc @@ -0,0 +1,291 @@ + + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbCommon.h" +#include "dbLayoutToNetlist.h" +#include "dbDeepRegion.h" +#include "dbShapeRepository.h" +#include "dbCellMapping.h" + +namespace db +{ + +static bool is_deep (const db::Region &r) +{ + return dynamic_cast (r.delegate ()) != 0; +} + +// the iterator provides the hierarchical selection (enabling/disabling cells etc.) +LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) + : m_iter (iter), m_netlist_extracted (false) +{ + // check the iterator + if (iter.has_complex_region () || iter.region () != db::Box::world ()) { + throw tl::Exception (tl::to_string (tr ("The netlist extractor cannot work on clipped layouts"))); + } + + m_dss.set_text_enlargement (1); + m_dss.set_text_property_name (tl::Variant ("LABEL")); +} + +void LayoutToNetlist::set_threads (int n) +{ + m_dss.set_threads (n); +} + +void LayoutToNetlist::set_area_ratio (double ar) +{ + m_dss.set_max_area_ratio (ar); +} + +void LayoutToNetlist::set_max_vertex_count (size_t n) +{ + m_dss.set_max_vertex_count (n); +} + +db::Region *LayoutToNetlist::make_layer (unsigned int layer_index) +{ + db::RecursiveShapeIterator si (m_iter); + si.set_layer (layer_index); + si.shape_flags (db::ShapeIterator::All); + return new db::Region (si, m_dss); +} + +db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index) +{ + db::RecursiveShapeIterator si (m_iter); + si.set_layer (layer_index); + si.shape_flags (db::ShapeIterator::Texts); + return new db::Region (si, m_dss); +} + +db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index) +{ + db::RecursiveShapeIterator si (m_iter); + si.set_layer (layer_index); + si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes); + return new db::Region (si, m_dss); +} + +void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! mp_netlist.get ()) { + mp_netlist.reset (new db::Netlist ()); + } + extractor.extract(m_dss, layers, mp_netlist.get ()); +} + +void LayoutToNetlist::connect (const db::Region &l) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! is_deep (l)) { + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in intra-layer connectivity for netlist extraction")))); + } + + // we need to keep a reference, so we can safely delete the region + db::DeepLayer dl (l); + m_dlrefs.insert (dl); + + m_conn.connect (dl.layer ()); +} + +void LayoutToNetlist::connect (const db::Region &a, const db::Region &b) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! is_deep (a)) { + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in inter-layer connectivity (first layer) for netlist extraction")))); + } + if (! is_deep (b)) { + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in inter-layer connectivity (second layer) for netlist extraction")))); + } + + // we need to keep a reference, so we can safely delete the region + db::DeepLayer dla (a), dlb (b); + m_dlrefs.insert (dla); + m_dlrefs.insert (dlb); + + m_conn.connect (dla.layer (), dlb.layer ()); +} + +void LayoutToNetlist::extract_netlist () +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! mp_netlist.get ()) { + mp_netlist.reset (new db::Netlist ()); + } + m_netex.extract_nets(m_dss, m_conn, mp_netlist.get ()); + m_netlist_extracted = true; +} + +const db::Layout *LayoutToNetlist::internal_layout () const +{ + return &m_dss.const_layout (); +} + +const db::Cell *LayoutToNetlist::internal_top_cell () const +{ + return &m_dss.const_initial_cell (); +} + +unsigned int LayoutToNetlist::layer_of (const db::Region ®ion) const +{ + const db::DeepRegion *dr = dynamic_cast (region.delegate ()); + if (! dr) { + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in netlist extraction")))); + } + return dr->deep_layer ().layer (); +} + +db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell) +{ + return m_dss.cell_mapping_to_original (0, &layout, cell.cell_index ()); +} + +db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell) +{ + db::CellMapping cm; + if (layout.cells () == 1) { + cm.create_single_mapping (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ()); + } else { + cm.create_from_geometry (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ()); + } + return cm; +} + +db::Netlist *LayoutToNetlist::netlist () const +{ + return mp_netlist.get (); +} + +const db::hier_clusters &LayoutToNetlist::net_clusters () const +{ + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + return m_netex.clusters (); +} + +db::Region LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const +{ + unsigned int lid = layer_of (of_layer); + db::Region res; + + if (! recursive) { + + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + db::cell_index_type ci = circuit->cell_index (); + + const db::local_cluster &lc = m_netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); + + for (db::local_cluster::shape_iterator s = lc.begin (lid); !s.at_end (); ++s) { + res.insert (*s); + } + + } else { + + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + db::cell_index_type ci = circuit->cell_index (); + + for (db::recursive_cluster_shape_iterator rci (m_netex.clusters (), lid, ci, net.cluster_id ()); !rci.at_end (); ++rci) { + res.insert (rci->obj ().transformed (db::ICplxTrans (rci->trans ()) * rci.trans ())); + } + + } + + return res; +} + +db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point) +{ + return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point); +} + +size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, db::cell_index_type &cell_index_found) +{ + db::Box local_box = trans.inverted () * test_cluster.bbox (); + + const db::local_clusters &lcc = net_clusters ().clusters_per_cell (cell->cell_index ()); + for (db::local_clusters::touching_iterator i = lcc.begin_touching (local_box); ! i.at_end (); ++i) { + const db::local_cluster &lc = *i; + if (lc.interacts (test_cluster, trans, m_conn)) { + cell_index_found = cell->cell_index (); + return lc.id (); + } + } + + for (db::Cell::touching_iterator i = cell->begin_touching (local_box); ! i.at_end (); ++i) { + db::ICplxTrans t = trans * i->complex_trans (); + size_t cluster_id = search_net (t, &internal_layout ()->cell (i->cell_index ()), test_cluster, cell_index_found); + if (cluster_id > 0) { + return cluster_id; + } + } + + return 0; +} + +db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point) +{ + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + tl_assert (mp_netlist.get ()); + + unsigned int layer = layer_of (of_region); + + // Prepare a test cluster + db::Box box (point - db::Vector (1, 1), point + db::Vector (1, 1)); + db::GenericRepository sr; + db::local_cluster test_cluster; + test_cluster.add (db::PolygonRef (db::Polygon (box), sr), layer); + + db::cell_index_type ci = 0; + size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, ci); + if (cluster_id > 0) { + + db::Circuit *circuit = mp_netlist->circuit_by_cell_index (ci); + tl_assert (circuit != 0); + + db::Net *net = circuit->net_by_cluster_id (cluster_id); + tl_assert (net != 0); + return net; + + } else { + return 0; + } +} + +} diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h new file mode 100644 index 000000000..d1678e433 --- /dev/null +++ b/src/db/db/dbLayoutToNetlist.h @@ -0,0 +1,260 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbLayout2Netlist +#define _HDR_dbLayout2Netlist + +#include "dbCommon.h" +#include "dbCellMapping.h" +#include "dbNetlistExtractor.h" +#include "dbNetlistDeviceExtractor.h" + +namespace db +{ + +/** + * @brief A generic framework for extracting netlists from layouts + * + * This class wraps various concepts from db::NetlistExtractor and db::NetlistDeviceExtractor + * and more. It is supposed to provide a framework for extracting a netlist from a layout. + * + * The use model of this class consists of five steps which need to be executed in this order. + * + * @ul + * @li Configuration: in this step, the LayoutToNetlist object is created and + * if required, configured. Methods to be used in this step are "set_threads", + * "set_area_ratio" or "set_max_vertex_count". The constructor for the LayoutToNetlist + * object receives a db::RecursiveShapeIterator object which basically supplies the + * hierarchy and the layout taken as input. + * @/li + * @li Preparation + * In this step, the device recognitions and extraction layers are drawn from + * the framework. Derived can now be computed using boolean operations. + * Methods to use in this step are "make_layer" and it's variants. + * Layer preparation is not necessarily required to happen before all + * other steps. Layers can be computed shortly before they are required. + * @/li + * @li Following the preparation, the devices can be extracted using "extract_devices". + * This method needs to be called for each device extractor required. Each time, + * a device extractor needs to be given plus a map of device layers. The device + * layers are device extractor specific. Either original or derived layers + * may be specified here. Layer preparation may happen between calls to "extract_devices". + * @/li + * @li Once the devices are derived, the netlist connectivity can be defined and the + * netlist extracted. The connectivity is defined with "connect" and it's + * flavours. The actual netlist extraction happens with "extract_netlist". + * @/li + * @li After netlist extraction, the information is ready to be retrieved. + * The produced netlist is available with "netlist". The Shapes of a + * specific net are available with "shapes_of_net". "probe_net" allows + * finding a net by probing a specific location. + * @li + */ +class DB_PUBLIC LayoutToNetlist + : public gsi::ObjectBase, public tl::Object +{ +public: + /** + * @brief The constructor + * + * See the class description for details. + */ + LayoutToNetlist (const db::RecursiveShapeIterator &iter); + + /** + * @brief Sets the number of threads to use for operations which support multiple threads + */ + void set_threads (int n); + + /** + * @brief Sets the area_ratio parameter for the hierarchical network processor + * This parameter controls splitting of large polygons in order to reduce the + * error made by the bounding box approximation. + */ + void set_area_ratio (double ar); + + /** + * @brief Sets the max_vertex_count parameter for the hierarchical network processor + * This parameter controls splitting of large polygons in order to enhance performance + * for very big polygons. + */ + void set_max_vertex_count (size_t n); + + /** + * @brief Creates a new region representing an original layer + * "layer_index" is the layer index of the desired layer in the original layout. + * The Region object returned is a new object and must be deleted by the caller. + * This variant produces polygons and takes texts for net name annotation. + * A variant not taking texts is "make_polygon_layer". A Variant only taking + * texts is "make_text_layer". + */ + db::Region *make_layer (unsigned int layer_index); + + /** + * @brief Creates a new region representing an original layer taking texts only + * See "make_layer" for details. + */ + db::Region *make_text_layer (unsigned int layer_index); + + /** + * @brief Creates a new region representing an original layer taking polygons and texts + * See "make_layer" for details. + */ + db::Region *make_polygon_layer (unsigned int layer_index); + + /** + * @brief Extracts devices + * See the class description for more details. + * This method will run device extraction for the given extractor. The layer map is specific + * for the extractor and uses the region objects derived with "make_layer" and it's variants. + * + * In addition, derived regions can be passed too. Certain limitations apply. It's safe to use + * boolean operations for deriving layers. Other operations are applicable as long as they are + * capable of delivering hierarchical layers. + * + * If errors occur, the device extractor will contain theses errors. + */ + void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers); + + /** + * @brief Defines an intra-layer connection for the given layer. + * The layer is either an original layer created with "make_layer" and it's variants or + * a derived layer. Certain limitations apply. It's safe to use + * boolean operations for deriving layers. Other operations are applicable as long as they are + * capable of delivering hierarchical layers. + */ + void connect (const db::Region &l); + + /** + * @brief Defines an inter-layer connection for the given layers. + * The conditions mentioned with intra-layer "connect" apply for this method too. + */ + void connect (const db::Region &a, const db::Region &b); + + /** + * @brief Runs the netlist extraction + * See the class description for more details. + */ + void extract_netlist (); + + /** + * @brief Gets the internal layout + */ + const db::Layout *internal_layout () const; + + /** + * @brief Gets the internal top cell + */ + const db::Cell *internal_top_cell () const; + + /** + * @brief Gets the internal layer for a given extraction layer + * This method is required to derive the internal layer index - for example for + * investigating the cluster tree. + */ + unsigned int layer_of (const db::Region ®ion) const; + + /** + * @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout. + * CAUTION: may create new cells in "layout". + */ + db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell); + + /** + * @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout. + * This version will not create new cells in the target layout. + * If the required cells do not exist there yet, flatting will happen. + */ + db::CellMapping const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell); + + /** + * @brief gets the netlist extracted (0 if no extraction happened yet) + */ + db::Netlist *netlist () const; + + /** + * @brief Gets the hierarchical shape clusters derived in the net extraction. + * NOTE: the layer and cell indexes used inside this structure refer to the + * internal layout. + */ + const db::hier_clusters &net_clusters () const; + + /** + * @brief Returns all shapes of a specific net and layer. + * If "recursive" is true, the returned region will contain the shapes of + * all subcircuits too. + */ + db::Region shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const; + + /** + * @brief Finds the net by probing a specific location on the given layer + * + * This method will find a net looking at the given layer at the specific position. + * It will traverse the hierarchy below if no shape in the requested layer is found + * in the specified location. + * + * If no net is found at all, 0 is returned. + * + * This variant accepts a micrometer-unit location. The location is given in the + * coordinate space of the initial cell. + */ + db::Net *probe_net (const db::Region &of_region, const db::DPoint &point); + + /** + * @brief Finds the net by probing a specific location on the given layer + * See the description of the other "probe_net" variant. + * This variant accepts a database-unit location. The location is given in the + * coordinate space of the initial cell. + */ + db::Net *probe_net (const db::Region &of_region, const db::Point &point); + +private: + // no copying + LayoutToNetlist (const db::LayoutToNetlist &other); + LayoutToNetlist &operator= (const db::LayoutToNetlist &other); + + db::RecursiveShapeIterator m_iter; + db::DeepShapeStore m_dss; + db::Connectivity m_conn; + db::NetlistExtractor m_netex; + std::auto_ptr mp_netlist; + std::set m_dlrefs; + bool m_netlist_extracted; + + size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, db::cell_index_type &cell_index_found); +}; + +} + +namespace tl +{ + +template<> struct tl::type_traits : public tl::type_traits +{ + // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index ab089c80d..8684cd651 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -186,7 +186,7 @@ public: * device extraction. See the virtual methods below. */ class DB_PUBLIC NetlistDeviceExtractor - : public gsi::ObjectBase + : public gsi::ObjectBase, public tl::Object { public: typedef std::list error_list; diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 18173f2ff..6c1bfd0ed 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -360,6 +360,28 @@ RecursiveShapeIterator::confine_region (const region_type ®ion) m_needs_reinit = true; } +void +RecursiveShapeIterator::set_layer (unsigned int layer) +{ + if (m_has_layers || m_layer != layer) { + m_has_layers = false; + m_layers.clear (); + m_layer = layer; + m_needs_reinit = true; + } +} + +void +RecursiveShapeIterator::set_layers (const std::vector &layers) +{ + if (! m_has_layers || m_layers != layers) { + m_has_layers = true; + m_layers = layers; + m_layer = 0; + m_needs_reinit = true; + } +} + namespace { struct BoxTreePusher diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 59cc5679f..6f0a205e0 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -474,6 +474,20 @@ public: } } + /** + * @brief Changes the layer to be traversed + * + * This method must be used before shapes are being retrieved. Using this method may reset the iterator. + */ + void set_layer (unsigned int layer); + + /** + * @brief Changes the layers to be traversed + * + * This method must be used before shapes are being retrieved. Using this method may reset the iterator. + */ + void set_layers (const std::vector &layers); + /** * @brief Specify the property selector * diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc new file mode 100644 index 000000000..287f874b5 --- /dev/null +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -0,0 +1,351 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbNetlistDeviceExtractorClasses.h" +#include "dbLayoutToNetlist.h" +#include "dbStream.h" +#include "dbDeepRegion.h" +#include "dbDeepShapeStore.h" +#include "dbReader.h" +#include "dbWriter.h" +#include "dbCommonReader.h" +#include "dbTestSupport.h" + +#include "tlUnitTest.h" +#include "tlString.h" +#include "tlFileUtils.h" + +#include +#include + +namespace +{ + +static std::string device_name (const db::Device &device) +{ + if (device.name ().empty ()) { + return "$" + tl::to_string (device.id ()); + } else { + return device.name (); + } +} + +class MOSFETExtractor + : public db::NetlistDeviceExtractorMOS3Transistor +{ +public: + MOSFETExtractor (const std::string &name, db::Layout *debug_out) + : db::NetlistDeviceExtractorMOS3Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) + { + if (mp_debug_out) { + m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); + m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); + } + } + +private: + db::Layout *mp_debug_out; + unsigned int m_ldiff, m_lgate; + + void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) + { + if (! mp_debug_out) { + return; + } + + std::string cn = layout ()->cell_name (cell_index ()); + std::pair target_cp = mp_debug_out->cell_by_name (cn.c_str ()); + tl_assert (target_cp.first); + + db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->circuit ()->name () + "_" + device_name (*device)).c_str ()); + mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ())); + + db::Cell &device_cell = mp_debug_out->cell (dci); + for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) { + device_cell.shapes (m_ldiff).insert (*p); + } + for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) { + device_cell.shapes (m_lgate).insert (*p); + } + + std::string ps; + const std::vector &pd = device->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (! ps.empty ()) { + ps += ","; + } + ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ())); + } + device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ()))); + } +}; + +} + +static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +{ + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + + db::Cell &cell = ly.cell (cmap.cell_mapping (c->cell_index ())); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + const db::local_cluster &lc = clusters.clusters_per_cell (c->cell_index ()).cluster_by_id (n->cluster_id ()); + + bool any_shapes = false; + for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any_shapes; ++m) { + any_shapes = ! lc.begin (m->first).at_end (); + } + + if (any_shapes) { + + std::string nn = "NET_" + c->name () + "_" + n->expanded_name (); + db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ())); + cell.insert (db::CellInstArray (db::CellInst (net_cell.cell_index ()), db::Trans ())); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + db::Shapes &target = net_cell.shapes (m->second); + for (db::local_cluster::shape_iterator s = lc.begin (m->first); !s.at_end (); ++s) { + target.insert (*s); + } + } + + } + + } + + } +} + +static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) +{ + unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +TEST(1_Basic) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell)); + std::auto_ptr ractive (l2n.make_layer (active)); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + + // derived regions + + db::Region rpactive = *ractive & *rnwell; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = *ractive - *rnwell; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + + // NOTE: the device extractor will add more debug layers for the transistors: + // 20/0 -> Diffusion + // 21/0 -> Gate + MOSFETExtractor pmos_ex ("PMOS", &ly); + MOSFETExtractor nmos_ex ("NMOS", &ly); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + std::map dump_map; + dump_map [l2n.layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [l2n.layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [l2n.layer_of (*rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [l2n.layer_of (*rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [l2n.layer_of (*rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [l2n.layer_of (*rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [l2n.layer_of (*rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [l2n.layer_of (*rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + dump_nets_to_layout (*l2n.netlist (), l2n.net_clusters (), ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" + " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" + " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" + " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 (IN=$I19,$2=(null),OUT=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 (IN=$I1,$2=(null),OUT=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 (IN=$I2,$2=(null),OUT=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 (IN=$I3,$2=(null),OUT=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 (IN=$I4,$2=(null),OUT=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 (IN=$I5,$2=(null),OUT=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1.gds"); + + db::compare_layouts (_this, ly, au); +} diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 2f50910af..6625e2c30 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -46,7 +46,9 @@ static unsigned int layer_of (const db::Region ®ion) { - return db::DeepLayer (region).layer (); + const db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + return dr->deep_layer ().layer (); } static std::string device_name (const db::Device &device) @@ -58,6 +60,9 @@ static std::string device_name (const db::Device &device) } } +namespace +{ + class MOSFETExtractor : public db::NetlistDeviceExtractorMOS3Transistor { @@ -108,6 +113,8 @@ private: } }; +} + static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) { unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); @@ -362,93 +369,3 @@ TEST(1_DeviceAndNetExtraction) db::compare_layouts (_this, ly, au); } - -#if 0 - -// -------------------------------------------------------------------------------------- -// An attempt to simplify things. - -/* -TODO: -- netlist query functions such as net_by_name, device_by_name, circuit_by_name -- terminal geometry (Polygon) for device, combined device geometry (all terminals) -- netlist manipulation methods (i.e. flatten certain cells, purging etc.) -*/ - -#include "tlGlobPattern.h" -#include "dbHierNetworkProcessor.h" - -namespace db -{ - -class DB_PUBLIC LayoutToNetlist -{ -public: - // the iterator provides the hierarchical selection (enabling/disabling cells etc.) - LayoutToNetlist (const db::RecursiveShapeIterator &iter); - - // --- Step 0: configuration - - void set_threads (unsigned int n); - void set_area_ratio (double ar); - void set_max_vertex_count (size_t n); - - // --- Step 1: preparation - - // returns new'd regions - db::Region *make_layer (unsigned int layer_index); - db::Region *make_text_layer (unsigned int layer_index); - db::Region *make_polygon_layer (unsigned int layer_index); - - // --- Step 2: device extraction - - // after this, the device extractor will have errors if some occured. - void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers); - - // --- Step 3: net extraction - - // define connectivity for the netlist extraction - void connect (const db::Region &l); - void connect (const db::Region &a, const db::Region &b); - - // runs the netlist extraction - void extract_netlist (); - - // --- Step 4: retrieval - - // gets the internal layout and cell (0 if not available) - const db::Layout *internal_layout () const; - const db::Cell *internal_top_cell () const; - - // gets the internal layer index of the given region - unsigned int layer_of (const db::Region ®ion) const; - - // creates a cell mapping for copying the internal hierarchy to the given layout - // CAUTION: may create new cells in "layout". - db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell); - - // creates a cell mapping for copying the internal hierarchy to the given layout - // This version will not create new cells in the target layout. - db::CellMapping const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell); - - // gets the netlist extracted (0 if no extraction happened yet) - db::Netlist *netlist () const; - - // gets the hierarchical clusters of the nets (CAUTION: the layer indexes therein are - // internal layer indexes), same for cell indexes. - // -> NOT GSI - const db::hier_clusters &net_clusters () const; - - // copies the shapes of the given net from a given layer - // (recursive true: include nets from subcircuits) - db::Region shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive); - - // finds the net by probing a specific location on the given layer looking through the - // hierarchy. Returns 0 if no net was found. - db::Net *probe_net (const db::Region &of_region, const db::DPoint &point); - db::Net *probe_net (const db::Region &of_region, const db::Point &point); -}; - -} - -#endif diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 18d308d82..67ab0f993 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -428,6 +428,12 @@ TEST(1a) x = collect(i5, g); EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + i5.set_layer (1); + x = collect_with_copy(i5, g); + EXPECT_EQ (x, "[$3](101,1;1101,1101)"); + x = collect(i5, g); + EXPECT_EQ (x, "[$3](101,1;1101,1101)"); + std::set ll; db::RecursiveShapeIterator i5a (g, c0, ll, db::Box::world ()); @@ -454,6 +460,14 @@ TEST(1a) EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); x = collect(i5cc, g, true); EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + + std::vector ll_new; + ll_new.push_back (0); + i5c.set_layers (ll_new); + x = collect_with_copy(i5c, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + x = collect(i5c, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); } TEST(1b) diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 693acf642..d1b455c68 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -63,7 +63,8 @@ SOURCES = \ dbNetlistTests.cc \ dbNetlistExtractorTests.cc \ dbNetlistDeviceExtractorTests.cc \ - dbNetlistDeviceClassesTests.cc + dbNetlistDeviceClassesTests.cc \ + dbLayoutToNetlistTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC From 72a140957de54d9cd9c8c1a962454398b02544f6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Dec 2018 18:22:45 +0100 Subject: [PATCH 153/335] WIP: added test for recursive net shape retrieval --- src/db/db/dbLayoutToNetlist.cc | 4 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 102 +++++++++++++----- .../algo/device_extract_au1_with_rec_nets.gds | Bin 0 -> 50894 bytes 3 files changed, 79 insertions(+), 27 deletions(-) create mode 100644 testdata/algo/device_extract_au1_with_rec_nets.gds diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index d0437d16d..f9b0f8c72 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -209,7 +209,7 @@ db::Region LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region const db::local_cluster &lc = m_netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); for (db::local_cluster::shape_iterator s = lc.begin (lid); !s.at_end (); ++s) { - res.insert (*s); + res.insert (s->obj ().transformed (s->trans ())); } } else { @@ -220,7 +220,7 @@ db::Region LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region db::cell_index_type ci = circuit->cell_index (); for (db::recursive_cluster_shape_iterator rci (m_netex.clusters (), lid, ci, net.cluster_id ()); !rci.at_end (); ++rci) { - res.insert (rci->obj ().transformed (db::ICplxTrans (rci->trans ()) * rci.trans ())); + res.insert (rci->obj ().transformed (rci.trans () * db::ICplxTrans (rci->trans ()))); } } diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 287f874b5..bebbd6e0c 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -101,34 +101,74 @@ private: } -static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +static void dump_nets_to_layout (const db::LayoutToNetlist &l2n, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) { + const db::Netlist &nl = *l2n.netlist (); for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { db::Cell &cell = ly.cell (cmap.cell_mapping (c->cell_index ())); for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - const db::local_cluster &lc = clusters.clusters_per_cell (c->cell_index ()).cluster_by_id (n->cluster_id ()); + db::cell_index_type nci = std::numeric_limits::max (); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + + db::Region shapes = l2n.shapes_of_net (*n, *m->first, false); + if (shapes.empty ()) { + continue; + } + + if (nci == std::numeric_limits::max ()) { + std::string nn = "NET_" + c->name () + "_" + n->expanded_name (); + nci = ly.add_cell (nn.c_str ()); + cell.insert (db::CellInstArray (db::CellInst (nci), db::Trans ())); + } + + shapes.insert_into (&ly, nci, m->second); - bool any_shapes = false; - for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any_shapes; ++m) { - any_shapes = ! lc.begin (m->first).at_end (); } - if (any_shapes) { + } - std::string nn = "NET_" + c->name () + "_" + n->expanded_name (); - db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ())); - cell.insert (db::CellInstArray (db::CellInst (net_cell.cell_index ()), db::Trans ())); + } +} - for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { - db::Shapes &target = net_cell.shapes (m->second); - for (db::local_cluster::shape_iterator s = lc.begin (m->first); !s.at_end (); ++s) { - target.insert (*s); - } +static void dump_recursive_nets_to_layout (const db::LayoutToNetlist &l2n, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +{ + const db::Netlist &nl = *l2n.netlist (); + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + + db::Cell &cell = ly.cell (cmap.cell_mapping (c->cell_index ())); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + // only handle nets without outgoing pins - these are local + bool is_local = true; + for (db::Net::const_pin_iterator p = n->begin_pins (); p != n->end_pins () && is_local; ++p) { + is_local = (p->subcircuit () != 0); + } + if (! is_local) { + continue; + } + + db::cell_index_type nci = std::numeric_limits::max (); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + + db::Region shapes = l2n.shapes_of_net (*n, *m->first, true); + if (shapes.empty ()) { + continue; } + if (nci == std::numeric_limits::max ()) { + std::string nn = "RNET_" + c->name () + "_" + n->expanded_name (); + nci = ly.add_cell (nn.c_str ()); + cell.insert (db::CellInstArray (db::CellInst (nci), db::Trans ())); + } + + shapes.insert_into (&ly, nci, m->second); + } } @@ -274,19 +314,31 @@ TEST(1_Basic) // 208/0 -> Metal2 // 210/0 -> N source/drain // 211/0 -> P source/drain - std::map dump_map; - dump_map [l2n.layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); - dump_map [l2n.layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0)); - dump_map [l2n.layer_of (*rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); - dump_map [l2n.layer_of (*rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); - dump_map [l2n.layer_of (*rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); - dump_map [l2n.layer_of (*rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); - dump_map [l2n.layer_of (*rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); - dump_map [l2n.layer_of (*rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); // write nets to layout db::CellMapping cm = l2n.cell_mapping_into (ly, tc); - dump_nets_to_layout (*l2n.netlist (), l2n.net_clusters (), ly, dump_map, cm); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string EXPECT_EQ (l2n.netlist ()->to_string (), @@ -345,7 +397,7 @@ TEST(1_Basic) std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); au = tl::combine_path (au, "algo"); - au = tl::combine_path (au, "device_extract_au1.gds"); + au = tl::combine_path (au, "device_extract_au1_with_rec_nets.gds"); db::compare_layouts (_this, ly, au); } diff --git a/testdata/algo/device_extract_au1_with_rec_nets.gds b/testdata/algo/device_extract_au1_with_rec_nets.gds new file mode 100644 index 0000000000000000000000000000000000000000..c61fa0fb5d4a1254e2dc0b4a383984492ee51f7f GIT binary patch literal 50894 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 Date: Sun, 30 Dec 2018 18:44:30 +0100 Subject: [PATCH 154/335] WIP: introduced Circuit::is_external_net --- src/db/db/dbNetlist.cc | 15 +++++++++++++++ src/db/db/dbNetlist.h | 7 +++++++ src/db/db/gsiDeclDbNetlist.cc | 4 ++++ src/db/unit_tests/dbLayoutToNetlistTests.cc | 6 +----- src/db/unit_tests/dbNetlistTests.cc | 3 +++ testdata/ruby/dbNetlist.rb | 2 ++ 6 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 1f740ef0f..392814066 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -851,6 +851,21 @@ void Circuit::connect_pin (size_t pin_id, Net *net) } } +bool Circuit::is_external_net (const db::Net *net) const +{ + if (!net || net->pin_count () == 0) { + return false; + } + + for (std::vector::const_iterator p = m_pin_refs.begin (); p != m_pin_refs.end (); ++p) { + if (*p != Net::pin_iterator () && (*p)->net () == net) { + return true; + } + } + + return false; +} + void Circuit::purge_nets () { std::vector nets_to_be_purged; diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index b1b627ffc..54b6a5ab1 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -1518,6 +1518,13 @@ public: */ void connect_pin (size_t pin_id, Net *net); + /** + * @brief Returns true, if the net is an external net + * + * External nets are net which are connected to an outgoing pin. + */ + bool is_external_net (const db::Net *net) const; + /** * @brief Purge unused nets * diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 194ad572e..f644ca07f 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -760,6 +760,10 @@ Class decl_dbCircuit ("db", "Circuit", "@brief Gets the cell index of the circuit\n" "See \\cell_index= for details.\n" ) + + gsi::method ("is_external_net?", &db::Circuit::is_external_net, gsi::arg ("net"), + "@brief Returns true, if the given net is an external one.\n" + "External nets are nets which are connected to an outgoing pin." + ) + gsi::method ("net_for_pin", (db::Net *(db::Circuit::*) (size_t)) &db::Circuit::net_for_pin, gsi::arg ("pin_id"), "@brief Gets the net object attached to a specific pin.\n" "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index bebbd6e0c..f450c71a9 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -144,11 +144,7 @@ static void dump_recursive_nets_to_layout (const db::LayoutToNetlist &l2n, db::L for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { // only handle nets without outgoing pins - these are local - bool is_local = true; - for (db::Net::const_pin_iterator p = n->begin_pins (); p != n->end_pins () && is_local; ++p) { - is_local = (p->subcircuit () != 0); - } - if (! is_local) { + if (c->is_external_net (n.operator-> ())) { continue; } diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index ad9ab0407..74e4d2acc 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -751,18 +751,21 @@ TEST(8_NetSubCircuitsEditing) n2->set_name ("n2"); c.add_net (n2); + EXPECT_EQ (c.is_external_net (n1), false); c.connect_pin (0, n1); EXPECT_EQ (n1->terminal_count (), size_t (0)); EXPECT_EQ (n1->pin_count (), size_t (1)); EXPECT_EQ (n1->is_floating (), true); EXPECT_EQ (n1->is_internal (), false); + EXPECT_EQ (c.is_external_net (n1), true); EXPECT_EQ (c.net_for_pin (0), n1); EXPECT_EQ (c.net_for_pin (1), 0); sc1->connect_pin (0, n1); sc1->connect_pin (1, n2); + EXPECT_EQ (c.is_external_net (n2), false); EXPECT_EQ (n1->terminal_count (), size_t (0)); EXPECT_EQ (n1->pin_count (), size_t (2)); diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 214983856..39c85f047 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -510,7 +510,9 @@ class DBNetlist_TestClass < TestBase c.each_net { |n| names << n.name } assert_equal(names, [ "NET1", "NET2" ]) + assert_equal(c.is_external_net?(net1), false) c.connect_pin(pina1, net1) + assert_equal(c.is_external_net?(net1), true) c.connect_pin(pinb1.id, net1) c.connect_pin(pina2, net2) c.connect_pin(pinb2.id, net2) From 9c607d7663c7c136c64c1a4c2326ee6b685f8b50 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Dec 2018 22:37:31 +0100 Subject: [PATCH 155/335] Added a first version of the layout to netlist extraction feature The main entry point is RBA::LayoutToNetlist which is the GSI binding for the layout to netlist extractor. For a first impression about the abilities of this extractor see the Ruby tests in testdata/ruby/dbLayoutToNetlist.rb. The framework itself consists of many classes, specifically - RBA::Netlist for the netlist representation - RBA::DeviceClass and superclasses (e.g. RBA::DeviceClassResistor and RBA::DeviceClassMOS3Transistor) for the description of devices. - RBA::DeviceExtractor and superclasses (i.e. RBA::DeviceExtractorMOS3Transistor or the generic RBA::GenericDeviceExtractor) for the implementation of the device extraction. - RBA::Connectivity for the description of inter- and intra-layer connections. --- src/db/db/db.pro | 3 +- src/db/db/dbLayoutToNetlist.cc | 103 ++++++- src/db/db/dbLayoutToNetlist.h | 17 +- src/db/db/dbNetlist.cc | 88 +++--- src/db/db/dbNetlist.h | 13 + src/db/db/gsiDeclDbLayoutToNetlist.cc | 212 ++++++++++++++ src/db/db/gsiDeclDbNetlist.cc | 5 + src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 48 ++-- src/db/unit_tests/dbLayoutToNetlistTests.cc | 33 +++ src/rba/unit_tests/rba.cc | 1 + testdata/ruby/dbLayoutToNetlist.rb | 287 +++++++++++++++++++ testdata/ruby/dbNetlistDeviceClasses.rb | 2 +- 12 files changed, 733 insertions(+), 79 deletions(-) create mode 100644 src/db/db/gsiDeclDbLayoutToNetlist.cc create mode 100644 testdata/ruby/dbLayoutToNetlist.rb diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 13ac344f1..9d78787ca 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -152,7 +152,8 @@ SOURCES = \ gsiDeclDbNetlistDeviceExtractor.cc \ gsiDeclDbHierNetworkProcessor.cc \ dbNetlistDeviceExtractorClasses.cc \ - dbLayoutToNetlist.cc + dbLayoutToNetlist.cc \ + gsiDeclDbLayoutToNetlist.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index f9b0f8c72..4ac11e41c 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -53,16 +53,31 @@ void LayoutToNetlist::set_threads (int n) m_dss.set_threads (n); } +int LayoutToNetlist::threads () const +{ + return m_dss.threads (); +} + void LayoutToNetlist::set_area_ratio (double ar) { m_dss.set_max_area_ratio (ar); } +double LayoutToNetlist::area_ratio () const +{ + return m_dss.max_area_ratio (); +} + void LayoutToNetlist::set_max_vertex_count (size_t n) { m_dss.set_max_vertex_count (n); } +size_t LayoutToNetlist::max_vertex_count () const +{ + return m_dss.max_vertex_count (); +} + db::Region *LayoutToNetlist::make_layer (unsigned int layer_index) { db::RecursiveShapeIterator si (m_iter); @@ -233,25 +248,32 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoi return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point); } -size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, db::cell_index_type &cell_index_found) +size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path) { - db::Box local_box = trans.inverted () * test_cluster.bbox (); + db::Box local_box = trans * test_cluster.bbox (); const db::local_clusters &lcc = net_clusters ().clusters_per_cell (cell->cell_index ()); for (db::local_clusters::touching_iterator i = lcc.begin_touching (local_box); ! i.at_end (); ++i) { const db::local_cluster &lc = *i; if (lc.interacts (test_cluster, trans, m_conn)) { - cell_index_found = cell->cell_index (); return lc.id (); } } for (db::Cell::touching_iterator i = cell->begin_touching (local_box); ! i.at_end (); ++i) { - db::ICplxTrans t = trans * i->complex_trans (); - size_t cluster_id = search_net (t, &internal_layout ()->cell (i->cell_index ()), test_cluster, cell_index_found); - if (cluster_id > 0) { - return cluster_id; + + for (db::CellInstArray::iterator ia = i->begin_touching (local_box, internal_layout ()); ! ia.at_end (); ++ia) { + + db::ICplxTrans trans_inst = i->complex_trans (*ia); + db::ICplxTrans t = trans_inst.inverted () * trans; + size_t cluster_id = search_net (t, &internal_layout ()->cell (i->cell_index ()), test_cluster, rev_inst_path); + if (cluster_id > 0) { + rev_inst_path.push_back (db::InstElement (*i, ia)); + return cluster_id; + } + } + } return 0; @@ -264,6 +286,9 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin } tl_assert (mp_netlist.get ()); + db::CplxTrans dbu_trans (internal_layout ()->dbu ()); + db::VCplxTrans dbu_trans_inv = dbu_trans.inverted (); + unsigned int layer = layer_of (of_region); // Prepare a test cluster @@ -272,15 +297,69 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin db::local_cluster test_cluster; test_cluster.add (db::PolygonRef (db::Polygon (box), sr), layer); - db::cell_index_type ci = 0; - size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, ci); + std::vector inst_path; + + size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, inst_path); if (cluster_id > 0) { - db::Circuit *circuit = mp_netlist->circuit_by_cell_index (ci); - tl_assert (circuit != 0); + // search_net delivers the path in reverse order + std::reverse (inst_path.begin (), inst_path.end ()); + + std::vector cell_indexes; + cell_indexes.reserve (inst_path.size () + 1); + cell_indexes.push_back (internal_top_cell ()->cell_index ()); + for (std::vector::const_iterator i = inst_path.begin (); i != inst_path.end (); ++i) { + cell_indexes.push_back (i->inst_ptr.cell_index ()); + } + + db::Circuit *circuit = mp_netlist->circuit_by_cell_index (cell_indexes.back ()); + if (! circuit) { + // the circuit has probably been optimized away + return 0; + } db::Net *net = circuit->net_by_cluster_id (cluster_id); - tl_assert (net != 0); + if (! net) { + // the net has probably been optimized away + return 0; + } + + // follow the path up in the net hierarchy using the transformation and the upper cell index as the + // guide line + while (! inst_path.empty () && circuit->is_external_net (net)) { + + cell_indexes.pop_back (); + + db::Pin *pin = 0; + for (db::Circuit::pin_iterator p = circuit->begin_pins (); p != circuit->end_pins () && ! pin; ++p) { + if (circuit->net_for_pin (p->id ()) == net) { + pin = p.operator-> (); + } + } + tl_assert (pin != 0); + + db::DCplxTrans dtrans = dbu_trans * inst_path.back ().complex_trans () * dbu_trans_inv; + + // try to find a parent circuit which connects to this net + db::Circuit *upper_circuit = 0; + db::Net *upper_net = 0; + for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_net; ++r) { + if (r->trans ().equal (dtrans) && r->circuit () && r->circuit ()->cell_index () == cell_indexes.back ()) { + upper_net = r->net_for_pin (pin->id ()); + upper_circuit = r->circuit (); + } + } + + if (upper_net) { + circuit = upper_circuit; + net = upper_net; + inst_path.pop_back (); + } else { + break; + } + + } + return net; } else { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index d1678e433..4963439f2 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -85,6 +85,11 @@ public: */ void set_threads (int n); + /** + * @brief Gets the number of threads to use + */ + int threads () const; + /** * @brief Sets the area_ratio parameter for the hierarchical network processor * This parameter controls splitting of large polygons in order to reduce the @@ -92,6 +97,11 @@ public: */ void set_area_ratio (double ar); + /** + * @brief Gets the area ratio + */ + double area_ratio () const; + /** * @brief Sets the max_vertex_count parameter for the hierarchical network processor * This parameter controls splitting of large polygons in order to enhance performance @@ -99,6 +109,11 @@ public: */ void set_max_vertex_count (size_t n); + /** + * @brief Gets the max vertex count + */ + size_t max_vertex_count () const; + /** * @brief Creates a new region representing an original layer * "layer_index" is the layer index of the desired layer in the original layout. @@ -240,7 +255,7 @@ private: std::set m_dlrefs; bool m_netlist_extracted; - size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, db::cell_index_type &cell_index_found); + size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path); }; } diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 392814066..909cf7953 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -446,6 +446,15 @@ void Net::set_name (const std::string &name) } } +std::string Net::qname () const +{ + if (circuit ()) { + return circuit ()->name () + ":" + expanded_name (); + } else { + return expanded_name (); + } +} + std::string Net::expanded_name () const { if (name ().empty ()) { @@ -531,7 +540,9 @@ Circuit::Circuit () m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), m_index (0) { - // .. nothing yet .. + m_devices.changed ().add (this, &Circuit::devices_changed); + m_nets.changed ().add (this, &Circuit::nets_changed); + m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed); } Circuit::Circuit (const Circuit &other) @@ -544,6 +555,10 @@ Circuit::Circuit (const Circuit &other) m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), m_index (0) { + m_devices.changed ().add (this, &Circuit::devices_changed); + m_nets.changed ().add (this, &Circuit::nets_changed); + m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed); + operator= (other); } @@ -551,14 +566,9 @@ Circuit &Circuit::operator= (const Circuit &other) { if (this != &other) { - m_name = other.m_name; - m_device_by_id.invalidate (); - m_subcircuit_by_id.invalidate (); - m_net_by_cluster_id.invalidate (); - m_device_by_name.invalidate (); - m_subcircuit_by_name.invalidate (); - m_net_by_name.invalidate (); + clear (); + m_name = other.m_name; m_pins = other.m_pins; std::map device_table; @@ -630,6 +640,28 @@ const Pin *Circuit::pin_by_name (const std::string &name) const return 0; } +void Circuit::devices_changed () +{ + m_device_by_id.invalidate (); + m_device_by_name.invalidate (); +} + +void Circuit::subcircuits_changed () +{ + m_subcircuit_by_id.invalidate (); + m_subcircuit_by_name.invalidate (); + + if (mp_netlist) { + mp_netlist->invalidate_topology (); + } +} + +void Circuit::nets_changed () +{ + m_net_by_cluster_id.invalidate (); + m_net_by_name.invalidate (); +} + void Circuit::clear () { m_name.clear (); @@ -637,12 +669,6 @@ void Circuit::clear () m_devices.clear (); m_nets.clear (); m_subcircuits.clear (); - m_device_by_id.invalidate (); - m_subcircuit_by_id.invalidate (); - m_net_by_cluster_id.invalidate (); - m_device_by_name.invalidate (); - m_subcircuit_by_name.invalidate (); - m_net_by_name.invalidate (); } void Circuit::set_name (const std::string &name) @@ -720,15 +746,11 @@ void Circuit::add_net (Net *net) { m_nets.push_back (net); net->set_circuit (this); - m_net_by_cluster_id.invalidate (); - m_net_by_name.invalidate (); } void Circuit::remove_net (Net *net) { m_nets.erase (net); - m_net_by_cluster_id.invalidate (); - m_net_by_name.invalidate (); } void Circuit::add_device (Device *device) @@ -743,15 +765,11 @@ void Circuit::add_device (Device *device) device->set_id (id + 1); m_devices.push_back (device); - m_device_by_id.invalidate (); - m_device_by_name.invalidate (); } void Circuit::remove_device (Device *device) { m_devices.erase (device); - m_device_by_id.invalidate (); - m_device_by_name.invalidate (); } void Circuit::add_subcircuit (SubCircuit *subcircuit) @@ -766,23 +784,11 @@ void Circuit::add_subcircuit (SubCircuit *subcircuit) subcircuit->set_id (id + 1); m_subcircuits.push_back (subcircuit); - m_subcircuit_by_id.invalidate (); - m_subcircuit_by_name.invalidate (); - - if (mp_netlist) { - mp_netlist->invalidate_topology (); - } } void Circuit::remove_subcircuit (SubCircuit *subcircuit) { m_subcircuits.erase (subcircuit); - m_subcircuit_by_id.invalidate (); - m_subcircuit_by_name.invalidate (); - - if (mp_netlist) { - mp_netlist->invalidate_topology (); - } } void Circuit::register_ref (SubCircuit *r) @@ -1182,6 +1188,7 @@ Netlist::Netlist () m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits) { m_circuits.changed ().add (this, &Netlist::invalidate_topology); + m_circuits.changed ().add (this, &Netlist::circuits_changed); } Netlist::Netlist (const Netlist &other) @@ -1191,6 +1198,7 @@ Netlist::Netlist (const Netlist &other) { operator= (other); m_circuits.changed ().add (this, &Netlist::invalidate_topology); + m_circuits.changed ().add (this, &Netlist::circuits_changed); } Netlist &Netlist::operator= (const Netlist &other) @@ -1222,6 +1230,12 @@ Netlist &Netlist::operator= (const Netlist &other) return *this; } +void Netlist::circuits_changed () +{ + m_circuit_by_cell_index.invalidate (); + m_circuit_by_name.invalidate (); +} + void Netlist::invalidate_topology () { if (m_valid_topology) { @@ -1465,24 +1479,18 @@ void Netlist::clear () { m_device_classes.clear (); m_circuits.clear (); - m_circuit_by_name.invalidate (); - m_circuit_by_cell_index.invalidate (); } void Netlist::add_circuit (Circuit *circuit) { m_circuits.push_back (circuit); circuit->set_netlist (this); - m_circuit_by_name.invalidate (); - m_circuit_by_cell_index.invalidate (); } void Netlist::remove_circuit (Circuit *circuit) { circuit->set_netlist (0); m_circuits.erase (circuit); - m_circuit_by_name.invalidate (); - m_circuit_by_cell_index.invalidate (); } void Netlist::add_device_class (DeviceClass *device_class) diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 54b6a5ab1..9c4909200 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -459,6 +459,14 @@ public: */ std::string expanded_name () const; + /** + * @brief Gets the qualified name + * + * The qualified name is like the expanded name, but preceeded with the + * Circuit name if known (e.g. "CIRCUIT:NET") + */ + std::string qname () const; + /** * @brief Sets the cluster ID of this net * @@ -1578,6 +1586,10 @@ private: void set_netlist (Netlist *netlist); bool combine_parallel_devices (const db::DeviceClass &cls); bool combine_serial_devices (const db::DeviceClass &cls); + + void devices_changed (); + void subcircuits_changed (); + void nets_changed (); }; /** @@ -2269,6 +2281,7 @@ private: void invalidate_topology (); void validate_topology (); + void circuits_changed (); const tl::vector &child_circuits (Circuit *circuit); const tl::vector &parent_circuits (Circuit *circuit); diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc new file mode 100644 index 000000000..900c906f5 --- /dev/null +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -0,0 +1,212 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "gsiDecl.h" +#include "dbLayoutToNetlist.h" + +namespace gsi +{ + +static db::LayoutToNetlist *make_l2n (const db::RecursiveShapeIterator &iter) +{ + return new db::LayoutToNetlist (iter); +} + +static db::Layout *l2n_internal_layout (db::LayoutToNetlist *l2n) +{ + // although this isn't very clean, we dare to do so as const references are pretty useless in script languages. + return const_cast (l2n->internal_layout ()); +} + +static db::Cell *l2n_internal_top_cell (db::LayoutToNetlist *l2n) +{ + // although this isn't very clean, we dare to do so as const references are pretty useless in script languages. + return const_cast (l2n->internal_top_cell ()); +} + +Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", + gsi::constructor ("new", &make_l2n, gsi::arg ("iter"), + "@brief The constructor\n" + "See the class description for details.\n" + ) + + gsi::method ("threads=", &db::LayoutToNetlist::set_threads, gsi::arg ("n"), + "@brief Sets the number of threads to use for operations which support multiple threads\n" + ) + + gsi::method ("threads", &db::LayoutToNetlist::threads, + "@brief Gets the number of threads to use for operations which support multiple threads\n" + ) + + gsi::method ("area_ratio=", &db::LayoutToNetlist::set_area_ratio, gsi::arg ("r"), + "@brief Sets the area_ratio parameter for the hierarchical network processor\n" + "This parameter controls splitting of large polygons in order to reduce the\n" + "error made by the bounding box approximation.\n" + ) + + gsi::method ("area_ratio", &db::LayoutToNetlist::area_ratio, + "@brief Gets the area_ratio parameter for the hierarchical network processor\n" + "See \\area_ratio= for details about this attribute." + ) + + gsi::method ("max_vertex_count=", &db::LayoutToNetlist::set_max_vertex_count, gsi::arg ("n"), + "@brief Sets the max_vertex_count parameter for the hierarchical network processor\n" + "This parameter controls splitting of large polygons in order to enhance performance\n" + "for very big polygons.\n" + ) + + gsi::method ("max_vertex_count", &db::LayoutToNetlist::max_vertex_count, + "See \\max_vertex_count= for details about this attribute." + ) + + gsi::method ("make_layer", &db::LayoutToNetlist::make_layer, gsi::arg ("layer_index"), + "@brief Creates a new region representing an original layer\n" + "'layer_index'' is the layer index of the desired layer in the original layout.\n" + "The Region object returned is a new object and must be deleted by the caller.\n" + "This variant produces polygons and takes texts for net name annotation.\n" + "A variant not taking texts is \\make_polygon_layer. A Variant only taking\n" + "texts is \\make_text_layer.\n""" + ) + + gsi::method ("make_text_layer", &db::LayoutToNetlist::make_text_layer, gsi::arg ("layer_index"), + "@brief Creates a new region representing an original layer taking texts only\n" + "See \\make_layer for details.\n" + ) + + gsi::method ("make_polygon_layer", &db::LayoutToNetlist::make_polygon_layer, gsi::arg ("layer_index"), + "@brief Creates a new region representing an original layer taking polygons and texts\n" + "See \\make_layer for details.\n" + ) + + gsi::method ("extract_devices", &db::LayoutToNetlist::extract_devices, gsi::arg ("extractor"), gsi::arg ("layers"), + "@brief Extracts devices\n" + "See the class description for more details.\n" + "This method will run device extraction for the given extractor. The layer map is specific\n" + "for the extractor and uses the region objects derived with \\make_layer and it's variants.\n" + "\n" + "In addition, derived regions can be passed too. Certain limitations apply. It's safe to use\n" + "boolean operations for deriving layers. Other operations are applicable as long as they are\n" + "capable of delivering hierarchical layers.\n" + "\n" + "If errors occur, the device extractor will contain theses errors.\n" + ) + + gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("l"), + "@brief Defines an intra-layer connection for the given layer.\n" + "The layer is either an original layer created with \\make_layer and it's variants or\n" + "a derived layer. Certain limitations apply. It's safe to use\n" + "boolean operations for deriving layers. Other operations are applicable as long as they are\n" + "capable of delivering hierarchical layers.\n" + ) + + gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &, const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("a"), gsi::arg ("b"), + "@brief Defines an inter-layer connection for the given layers.\n" + "The conditions mentioned with intra-layer \\connect apply for this method too.\n" + ) + + gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, + "@brief Runs the netlist extraction\n" + "See the class description for more details.\n" + ) + + gsi::method_ext ("internal_layout", &l2n_internal_layout, + "@brief Gets the internal layout\n" + "Usually it should not be required to obtain the internal layout. If you need to do so, make sure not to modify the layout as\n" + "the functionality of the netlist extractor depends on it." + ) + + gsi::method_ext ("internal_top_cell", &l2n_internal_top_cell, + "@brief Gets the internal top cell\n" + "Usually it should not be required to obtain the internal cell. If you need to do so, make sure not to modify the cell as\n" + "the functionality of the netlist extractor depends on it." + ) + + gsi::method ("layer_of", &db::LayoutToNetlist::layer_of, gsi::arg ("l"), + "@brief Gets the internal layer for a given extraction layer\n" + "This method is required to derive the internal layer index - for example for\n" + "investigating the cluster tree.\n" + ) + + gsi::method ("cell_mapping_into", &db::LayoutToNetlist::cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"), + "@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n" + "CAUTION: may create new cells in 'layout'.\n" + ) + + gsi::method ("const_cell_mapping_into", &db::LayoutToNetlist::const_cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"), + "@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n" + "This version will not create new cells in the target layout.\n" + "If the required cells do not exist there yet, flatting will happen.\n" + ) + + gsi::method ("netlist", &db::LayoutToNetlist::netlist, + "@brief gets the netlist extracted (0 if no extraction happened yet)\n" + ) + + gsi::method ("shapes_of_net", &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), + "@brief Returns all shapes of a specific net and layer.\n" + "If 'recursive'' is true, the returned region will contain the shapes of\n" + "all subcircuits too.\n" + ) + + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), + "@brief Finds the net by probing a specific location on the given layer\n" + "\n" + "This method will find a net looking at the given layer at the specific position.\n" + "It will traverse the hierarchy below if no shape in the requested layer is found\n" + "in the specified location. The function will report the topmost net from far above the\n" + "hierarchy of circuits as possible.\n" + "\n" + "If no net is found at all, 0 is returned.\n" + "\n" + "It is recommended to use \\probe on the netlist right after extraction.\n" + "Optimization functions such as \\Netlist#purge will remove parts of the net which means\n" + "shape to net probing may no longer work for these nets.\n" + "\n" + "This variant accepts a micrometer-unit location. The location is given in the\n" + "coordinate space of the initial cell.\n" + ) + + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::Point &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), + "@brief Finds the net by probing a specific location on the given layer\n" + "See the description of the other \\probe_net variant.\n" + "This variant accepts a database-unit location. The location is given in the\n" + "coordinate space of the initial cell.\n" + ), + "@brief A generic framework for extracting netlists from layouts\n" + "\n" + "This class wraps various concepts from db::NetlistExtractor and db::NetlistDeviceExtractor\n" + "and more. It is supposed to provide a framework for extracting a netlist from a layout.\n" + "\n" + "The use model of this class consists of five steps which need to be executed in this order.\n" + "\n" + "@ul\n" + "@li Configuration: in this step, the LayoutToNetlist object is created and\n" + " if required, configured. Methods to be used in this step are \\threads=,\n" + " \\area_ratio= or \\max_vertex_count=. The constructor for the LayoutToNetlist\n" + " object receives a \\RecursiveShapeIterator object which basically supplies the\n" + " hierarchy and the layout taken as input.\n" + "@/li\n" + "@li Preparation\n" + " In this step, the device recognitions and extraction layers are drawn from\n" + " the framework. Derived can now be computed using boolean operations.\n" + " Methods to use in this step are \\make_layer and it's variants.\n" + " Layer preparation is not necessarily required to happen before all\n" + " other steps. Layers can be computed shortly before they are required.\n" + "@/li\n" + "@li Following the preparation, the devices can be extracted using \\extract_devices.\n" + " This method needs to be called for each device extractor required. Each time,\n" + " a device extractor needs to be given plus a map of device layers. The device\n" + " layers are device extractor specific. Either original or derived layers\n" + " may be specified here. Layer preparation may happen between calls to \\extract_devices.\n" + "@/li\n" + "@li Once the devices are derived, the netlist connectivity can be defined and the\n" + " netlist extracted. The connectivity is defined with \\connect and it's\n" + " flavours. The actual netlist extraction happens with \\extract_netlist.\n" + "@/li\n" + "@li After netlist extraction, the information is ready to be retrieved.\n" + " The produced netlist is available with \\netlist. The Shapes of a\n" + " specific net are available with \\shapes_of_net. \\probe_net allows\n" + " finding a net by probing a specific location.\n" + "@li\n" + "\n" + "This class has been introduced in version 0.26." +); + +} diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index f644ca07f..10957428a 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -269,6 +269,11 @@ Class decl_dbNet ("db", "Net", "@brief Gets the name of the net.\n" "See \\name= for details about the name." ) + + gsi::method ("qname|to_s", &db::Net::qname, + "@brief Gets the qualified name.\n" + "The qualified name is like the expanded name, but the circuit's name is preceeded\n" + "(i.e. 'CIRCUIT:NET') if available.\n" + ) + gsi::method ("expanded_name", &db::Net::expanded_name, "@brief Gets the expanded name of the net.\n" "The expanded name takes the name of the net. If the name is empty, the cluster ID will be used to build a name. " diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index f56ffcb94..6c1400a32 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -29,11 +29,11 @@ namespace { /** * @brief A NetlistDeviceExtractor implementation that allows reimplementation of the virtual methods */ -class NetlistDeviceExtractorImpl +class GenericDeviceExtractor : public db::NetlistDeviceExtractor { public: - NetlistDeviceExtractorImpl () + GenericDeviceExtractor () : db::NetlistDeviceExtractor (std::string ()) { // .. nothing yet .. @@ -66,7 +66,7 @@ public: virtual void setup () { if (cb_setup.can_issue ()) { - cb_setup.issue (&NetlistDeviceExtractorImpl::setup_fb); + cb_setup.issue (&GenericDeviceExtractor::setup_fb); } else { db::NetlistDeviceExtractor::setup (); } @@ -80,7 +80,7 @@ public: virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector &layers) const { if (cb_get_connectivity.can_issue ()) { - return cb_get_connectivity.issue &> (&NetlistDeviceExtractorImpl::get_connectivity_fb, layout, layers); + return cb_get_connectivity.issue &> (&GenericDeviceExtractor::get_connectivity_fb, layout, layers); } else { return db::NetlistDeviceExtractor::get_connectivity (layout, layers); } @@ -94,7 +94,7 @@ public: virtual void extract_devices (const std::vector &layer_geometry) { if (cb_extract_devices.can_issue ()) { - cb_extract_devices.issue &> (&NetlistDeviceExtractorImpl::extract_devices_fb, layer_geometry); + cb_extract_devices.issue &> (&GenericDeviceExtractor::extract_devices_fb, layer_geometry); } else { db::NetlistDeviceExtractor::extract_devices (layer_geometry); } @@ -110,7 +110,7 @@ public: namespace tl { -template<> struct tl::type_traits : public tl::type_traits +template<> struct tl::type_traits : public tl::type_traits { // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor typedef tl::false_tag has_copy_constructor; @@ -235,11 +235,11 @@ Class decl_dbNetlistDeviceExtractor ("db", "DeviceEx "This class has been introduced in version 0.26." ); -Class decl_NetlistDeviceExtractorImpl (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractor", - gsi::method ("name=", &NetlistDeviceExtractorImpl::set_name, +Class decl_GenericDeviceExtractor (decl_dbNetlistDeviceExtractor, "db", "GenericDeviceExtractor", + gsi::method ("name=", &GenericDeviceExtractor::set_name, "@brief Sets the name of the device extractor and the device class." ) + - gsi::callback ("setup", &NetlistDeviceExtractorImpl::setup, &NetlistDeviceExtractorImpl::cb_setup, + gsi::callback ("setup", &GenericDeviceExtractor::setup, &GenericDeviceExtractor::cb_setup, "@brief Sets up the extractor.\n" "This method is supposed to set up the device extractor. This involves three basic steps:\n" "defining the name, the device classe and setting up the device layers.\n" @@ -248,7 +248,7 @@ Class decl_NetlistDeviceExtractorImpl (decl_dbNetlis "Use \\register_device_class to register the device class you need.\n" "Defined the layers by calling \\define_layer once or several times.\n" ) + - gsi::callback ("get_connectivity", &NetlistDeviceExtractorImpl::get_connectivity, &NetlistDeviceExtractorImpl::cb_get_connectivity, + gsi::callback ("get_connectivity", &GenericDeviceExtractor::get_connectivity, &GenericDeviceExtractor::cb_get_connectivity, gsi::arg ("layout"), gsi::arg ("layers"), "@brief Gets the connectivity object used to extract the device geometry.\n" "This method shall raise an error, if the input layer are not properly defined (e.g.\n" @@ -258,7 +258,7 @@ Class decl_NetlistDeviceExtractorImpl (decl_dbNetlis "The list of layers corresponds to the number of layers defined. Use the layer indexes from this list " "to build the connectivity with \\Connectivity#connect." ) + - gsi::callback ("extract_devices", &NetlistDeviceExtractorImpl::extract_devices, &NetlistDeviceExtractorImpl::cb_extract_devices, + gsi::callback ("extract_devices", &GenericDeviceExtractor::extract_devices, &GenericDeviceExtractor::cb_extract_devices, gsi::arg ("layer_geometry"), "@brief Extracts the devices from the given shape cluster.\n" "\n" @@ -271,7 +271,7 @@ Class decl_NetlistDeviceExtractorImpl (decl_dbNetlis "terminals by which the nets extracted in the network extraction step connect\n" "to the new devices.\n" ) + - gsi::method ("register_device_class", &NetlistDeviceExtractorImpl::register_device_class, gsi::arg ("device_class"), + gsi::method ("register_device_class", &GenericDeviceExtractor::register_device_class, gsi::arg ("device_class"), "@brief Registers a device class.\n" "The device class object will become owned by the netlist and must not be deleted by\n" "the caller. The name of the device class will be changed to the name given to\n" @@ -279,19 +279,19 @@ Class decl_NetlistDeviceExtractorImpl (decl_dbNetlis "This method shall be used inside the implementation of \\setup to register\n" "the device classes.\n" ) + - gsi::method ("define_layer", &NetlistDeviceExtractorImpl::define_layer, gsi::arg ("name"), gsi::arg ("description"), + gsi::method ("define_layer", &GenericDeviceExtractor::define_layer, gsi::arg ("name"), gsi::arg ("description"), "@brief Defines a layer.\n" "Each call will define one more layer for the device extraction.\n" "This method shall be used inside the implementation of \\setup to define\n" "the device layers. The actual geometries are later available to \\extract_devices\n" "in the order the layers are defined.\n" ) + - gsi::method ("create_device", &NetlistDeviceExtractorImpl::create_device, + gsi::method ("create_device", &GenericDeviceExtractor::create_device, "@brief Creates a device.\n" "The device object returned can be configured by the caller, e.g. set parameters.\n" "It will be owned by the netlist and must not be deleted by the caller.\n" ) + - gsi::method ("define_terminal", (void (NetlistDeviceExtractorImpl::*) (db::Device *, size_t, size_t, const db::Polygon &)) &NetlistDeviceExtractorImpl::define_terminal, + gsi::method ("define_terminal", (void (GenericDeviceExtractor::*) (db::Device *, size_t, size_t, const db::Polygon &)) &GenericDeviceExtractor::define_terminal, gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("shape"), "@brief Defines a device terminal.\n" "This method will define a terminal to the given device and the given terminal ID. \n" @@ -301,7 +301,7 @@ Class decl_NetlistDeviceExtractorImpl (decl_dbNetlis "This version produces a terminal with a shape given by the polygon. Note that the polygon is\n" "specified in database units.\n" ) + - gsi::method ("define_terminal", (void (NetlistDeviceExtractorImpl::*) (db::Device *, size_t, size_t, const db::Box &)) &NetlistDeviceExtractorImpl::define_terminal, + gsi::method ("define_terminal", (void (GenericDeviceExtractor::*) (db::Device *, size_t, size_t, const db::Box &)) &GenericDeviceExtractor::define_terminal, gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("shape"), "@brief Defines a device terminal.\n" "This method will define a terminal to the given device and the given terminal ID. \n" @@ -311,7 +311,7 @@ Class decl_NetlistDeviceExtractorImpl (decl_dbNetlis "This version produces a terminal with a shape given by the box. Note that the box is\n" "specified in database units.\n" ) + - gsi::method ("define_terminal", (void (NetlistDeviceExtractorImpl::*) (db::Device *, size_t, size_t, const db::Point &)) &NetlistDeviceExtractorImpl::define_terminal, + gsi::method ("define_terminal", (void (GenericDeviceExtractor::*) (db::Device *, size_t, size_t, const db::Point &)) &GenericDeviceExtractor::define_terminal, gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("point"), "@brief Defines a device terminal.\n" "This method will define a terminal to the given device and the given terminal ID. \n" @@ -321,30 +321,30 @@ Class decl_NetlistDeviceExtractorImpl (decl_dbNetlis "This version produces a point-like terminal. Note that the point is\n" "specified in database units.\n" ) + - gsi::method ("dbu", &NetlistDeviceExtractorImpl::dbu, + gsi::method ("dbu", &GenericDeviceExtractor::dbu, "@brief Gets the database unit\n" ) + - gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &)) &NetlistDeviceExtractorImpl::error, + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &)) &GenericDeviceExtractor::error, gsi::arg ("message"), "@brief Issues an error with the given message\n" ) + - gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const db::DPolygon &)) &NetlistDeviceExtractorImpl::error, + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const db::DPolygon &)) &GenericDeviceExtractor::error, gsi::arg ("message"), gsi::arg ("geometry"), "@brief Issues an error with the given message and micrometer-units polygon geometry\n" ) + - gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const db::Polygon &)) &NetlistDeviceExtractorImpl::error, + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const db::Polygon &)) &GenericDeviceExtractor::error, gsi::arg ("message"), gsi::arg ("geometry"), "@brief Issues an error with the given message and databse-unit polygon geometry\n" ) + - gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const std::string &, const std::string &)) &NetlistDeviceExtractorImpl::error, + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &)) &GenericDeviceExtractor::error, gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), "@brief Issues an error with the given category name and description, message\n" ) + - gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const std::string &, const std::string &, const db::DPolygon &)) &NetlistDeviceExtractorImpl::error, + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &, const db::DPolygon &)) &GenericDeviceExtractor::error, gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"), "@brief Issues an error with the given category name and description, message and micrometer-units polygon geometry\n" ) + - gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const std::string &, const std::string &, const db::Polygon &)) &NetlistDeviceExtractorImpl::error, + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &, const db::Polygon &)) &GenericDeviceExtractor::error, gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"), "@brief Issues an error with the given category name and description, message and databse-unit polygon geometry\n" ), diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index f450c71a9..2254c9b23 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -172,6 +172,12 @@ static void dump_recursive_nets_to_layout (const db::LayoutToNetlist &l2n, db::L } } +// TODO: may be useful elsewhere? +static std::string qnet_name (const db::Net *net) +{ + return net ? net->qname () : "(null)"; +} + static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) { unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); @@ -361,6 +367,19 @@ TEST(1_Basic) "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" ); + // do some probing before purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I39"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2"); + // doesn't do anything here, but we test that this does not destroy anything: l2n.netlist ()->combine_devices (); @@ -396,4 +415,18 @@ TEST(1_Basic) au = tl::combine_path (au, "device_extract_au1_with_rec_nets.gds"); db::compare_layouts (_this, ly, au); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + // the transistor which supplies this probe target has been optimized away by "purge". + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2:$2"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2"); } diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index 2df8e38bb..883df8009 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -108,6 +108,7 @@ RUBYTEST (dbLayout, "dbLayout.rb") RUBYTEST (dbLayoutTest, "dbLayoutTest.rb") RUBYTEST (dbLayoutDiff, "dbLayoutDiff.rb") RUBYTEST (dbLayoutQuery, "dbLayoutQuery.rb") +RUBYTEST (dbLayoutToNetlist, "dbLayoutToNetlist.rb") RUBYTEST (dbMatrix, "dbMatrix.rb") RUBYTEST (dbNetlist, "dbNetlist.rb") RUBYTEST (dbNetlistDeviceClasses, "dbNetlistDeviceClasses.rb") diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb new file mode 100644 index 000000000..59d821ab8 --- /dev/null +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -0,0 +1,287 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2018 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBLayoutToNetlist_TestClass < TestBase + + def test_1_Basic + + ly = RBA::Layout::new + ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds")) + + l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, [])) + + l2n.threads = 17 + l2n.max_vertex_count = 42 + l2n.area_ratio = 7.5 + assert_equal(l2n.threads, 17) + assert_equal(l2n.max_vertex_count, 42) + assert_equal(l2n.area_ratio, 7.5) + + r = l2n.make_layer(ly.layer(6, 0)) + + assert_not_equal(l2n.internal_layout.object_id, ly.object_id) + assert_equal(l2n.internal_layout.top_cell.name, ly.top_cell.name) + assert_equal(l2n.internal_top_cell.name, ly.top_cell.name) + + assert_not_equal(l2n.layer_of(r), ly.layer(6, 0)) # would be a strange coincidence ... + + cm = l2n.const_cell_mapping_into(ly, ly.top_cell) + (0 .. l2n.internal_layout.cells - 1).each do |ci| + assert_equal(l2n.internal_layout.cell(ci).name, ly.cell(cm.cell_mapping(ci)).name) + end + + ly2 = RBA::Layout::new + ly2.create_cell(ly.top_cell.name) + + cm = l2n.cell_mapping_into(ly2, ly2.top_cell) + assert_equal(ly2.cells, ly.cells) + (0 .. l2n.internal_layout.cells - 1).each do |ci| + assert_equal(l2n.internal_layout.cell(ci).name, ly2.cell(cm.cell_mapping(ci)).name) + end + + end + + def test_2_ShapesFromNet + + ly = RBA::Layout::new + ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds")) + + l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, [])) + + # only plain backend connectivity + + rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) ) + rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1) ) + rvia1 = l2n.make_polygon_layer( ly.layer(7, 0) ) + rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0) ) + rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1) ) + + # Intra-layer + l2n.connect(rmetal1) + l2n.connect(rvia1) + l2n.connect(rmetal2) + + # Inter-layer + l2n.connect(rmetal1, rvia1) + l2n.connect(rvia1, rmetal2) + l2n.connect(rmetal1, rmetal1_lbl) # attaches labels + l2n.connect(rmetal2, rmetal2_lbl) # attaches labels + + # Perform netlist extraction + l2n.extract_netlist + + assert_equal(l2n.netlist.to_s, < rpsd, "G" => rpgate, "P" => rpoly }) + + # NMOS transistor device extraction + nmos_ex = RBA::DeviceExtractorMOS3Transistor::new("NMOS") + l2n.extract_devices(nmos_ex, { "SD" => rnsd, "G" => rngate, "P" => rpoly }) + + # Define connectivity for netlist extraction + + # Intra-layer + l2n.connect(rpsd) + l2n.connect(rnsd) + l2n.connect(rpoly) + l2n.connect(rdiff_cont) + l2n.connect(rpoly_cont) + l2n.connect(rmetal1) + l2n.connect(rvia1) + l2n.connect(rmetal2) + + # Inter-layer + l2n.connect(rpsd, rdiff_cont) + l2n.connect(rnsd, rdiff_cont) + l2n.connect(rpoly, rpoly_cont) + l2n.connect(rpoly_cont, rmetal1) + l2n.connect(rdiff_cont, rmetal1) + l2n.connect(rmetal1, rvia1) + l2n.connect(rvia1, rmetal2) + l2n.connect(rpoly, rpoly_lbl) # attaches labels + l2n.connect(rmetal1, rmetal1_lbl) # attaches labels + l2n.connect(rmetal2, rmetal2_lbl) # attaches labels + + # Perform netlist extraction + l2n.extract_netlist + + assert_equal(l2n.netlist.to_s, < Date: Mon, 31 Dec 2018 00:00:15 +0100 Subject: [PATCH 156/335] Please picky compilers .. --- src/db/db/dbNetlistProperty.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/dbNetlistProperty.h b/src/db/db/dbNetlistProperty.h index b5d4aeb2b..be8d92ca1 100644 --- a/src/db/db/dbNetlistProperty.h +++ b/src/db/db/dbNetlistProperty.h @@ -39,7 +39,7 @@ namespace tl class Extractor; // specialization of tl::VariantUserClass for the purpose of NetlistProperty representation - template <> class DB_PUBLIC tl::VariantUserClass + template <> class DB_PUBLIC VariantUserClass : public tl::VariantUserClassBase { public: From b75125f7c227b09441b002689d323c72e1dc6c48 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 31 Dec 2018 00:50:43 +0100 Subject: [PATCH 157/335] Please picky compilers (once more) .. --- src/db/db/dbLayoutToNetlist.h | 2 +- src/db/db/dbNetlistDeviceExtractor.h | 2 +- src/db/db/dbNetlistDeviceExtractorClasses.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 4963439f2..8a9a8efbd 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -263,7 +263,7 @@ private: namespace tl { -template<> struct tl::type_traits : public tl::type_traits +template<> struct type_traits : public tl::type_traits { // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor typedef tl::false_tag has_copy_constructor; diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index 8684cd651..de7201669 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -481,7 +481,7 @@ private: namespace tl { -template<> struct tl::type_traits : public tl::type_traits +template<> struct type_traits : public tl::type_traits { // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor typedef tl::false_tag has_copy_constructor; diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.h b/src/db/db/dbNetlistDeviceExtractorClasses.h index 7d8e9e09f..32e2e538a 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.h +++ b/src/db/db/dbNetlistDeviceExtractorClasses.h @@ -70,7 +70,7 @@ protected: namespace tl { -template<> struct tl::type_traits : public tl::type_traits +template<> struct type_traits : public tl::type_traits { typedef tl::false_tag has_copy_constructor; typedef tl::false_tag has_default_constructor; From 0cc340cf4fc489deae1d4237567bc91278971601 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 31 Dec 2018 01:08:51 +0100 Subject: [PATCH 158/335] Once more being nice to picky compilers ... --- src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index 6c1400a32..45a22e247 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -110,7 +110,7 @@ public: namespace tl { -template<> struct tl::type_traits : public tl::type_traits +template<> struct type_traits : public tl::type_traits { // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor typedef tl::false_tag has_copy_constructor; From 509de593e63cc04cac92916230abf6f20931099f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 31 Dec 2018 01:42:55 +0100 Subject: [PATCH 159/335] Removed a compiler warning. --- .../unit_tests/dbNetlistDeviceClassesTests.cc | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc index 510f85ee6..37e346e84 100644 --- a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -77,7 +77,7 @@ TEST(1_SerialResistors) "Circuit (A=n1,B=n3):\n" " D r1 (A=n1,B=n3) [R=4]\n" ); -}; +} TEST(2_SerialResistors1Swapped) { @@ -128,7 +128,7 @@ TEST(2_SerialResistors1Swapped) "Circuit (A=n1,B=n3):\n" " D r1 (A=n1,B=n3) [R=4]\n" ); -}; +} TEST(3_SerialResistors1OtherSwapped) { @@ -179,7 +179,7 @@ TEST(3_SerialResistors1OtherSwapped) "Circuit (A=n1,B=n3):\n" " D r1 (A=n3,B=n1) [R=4]\n" ); -}; +} TEST(4_SerialResistors2Swapped) { @@ -230,7 +230,7 @@ TEST(4_SerialResistors2Swapped) "Circuit (A=n1,B=n3):\n" " D r1 (A=n3,B=n1) [R=4]\n" ); -}; +} TEST(5_SerialResistorsNoCombination) { @@ -284,7 +284,7 @@ TEST(5_SerialResistorsNoCombination) " D r1 (A=n1,B=n2) [R=1]\n" " D r2 (A=n2,B=n3) [R=3]\n" ); -}; +} TEST(6_ParallelResistors) { @@ -332,7 +332,7 @@ TEST(6_ParallelResistors) "Circuit (A=n1,B=n2):\n" " D r1 (A=n1,B=n2) [R=1.2]\n" ); -}; +} TEST(7_ParallelResistors1Swapped) { @@ -380,7 +380,7 @@ TEST(7_ParallelResistors1Swapped) "Circuit (A=n1,B=n2):\n" " D r1 (A=n2,B=n1) [R=1.2]\n" ); -}; +} TEST(8_ParallelResistors1OtherSwapped) { @@ -428,7 +428,7 @@ TEST(8_ParallelResistors1OtherSwapped) "Circuit (A=n1,B=n2):\n" " D r1 (A=n1,B=n2) [R=1.2]\n" ); -}; +} TEST(9_ParallelResistors2Swapped) { @@ -476,7 +476,7 @@ TEST(9_ParallelResistors2Swapped) "Circuit (A=n1,B=n2):\n" " D r1 (A=n2,B=n1) [R=1.2]\n" ); -}; +} TEST(10_ComplexRegistorCombination) { @@ -551,7 +551,7 @@ TEST(10_ComplexRegistorCombination) "Circuit (A=n1,B=n4):\n" " D r4 (A=n1,B=n4) [R=2]\n" ); -}; +} TEST(11_SerialInductors) { @@ -602,7 +602,7 @@ TEST(11_SerialInductors) "Circuit (A=n1,B=n3):\n" " D l1 (A=n1,B=n3) [L=4]\n" ); -}; +} TEST(12_ParallelInductors) { @@ -650,7 +650,7 @@ TEST(12_ParallelInductors) "Circuit (A=n1,B=n2):\n" " D l1 (A=n1,B=n2) [L=1.2]\n" ); -}; +} TEST(13_SerialCapacitors) { @@ -701,7 +701,7 @@ TEST(13_SerialCapacitors) "Circuit (A=n1,B=n3):\n" " D c1 (A=n1,B=n3) [C=1.2]\n" ); -}; +} TEST(14_ParallelCapacitors) { @@ -749,7 +749,7 @@ TEST(14_ParallelCapacitors) "Circuit (A=n1,B=n2):\n" " D c1 (A=n1,B=n2) [C=4]\n" ); -}; +} TEST(15_SerialDiodes) { @@ -803,7 +803,7 @@ TEST(15_SerialDiodes) " D d1 (A=n1,C=n2) [A=2]\n" " D d2 (A=n2,C=n3) [A=3]\n" ); -}; +} TEST(16_ParallelDiodes) { @@ -851,7 +851,7 @@ TEST(16_ParallelDiodes) "Circuit (A=n1,B=n2):\n" " D d1 (A=n1,C=n2) [A=4]\n" ); -}; +} TEST(17_AntiParallelDiodes) { @@ -902,7 +902,7 @@ TEST(17_AntiParallelDiodes) " D d1 (A=n1,C=n2) [A=1]\n" " D d2 (A=n2,C=n1) [A=3]\n" ); -}; +} TEST(20_ParallelMOS3Transistors) { @@ -963,7 +963,7 @@ TEST(20_ParallelMOS3Transistors) "Circuit (A=n1,B=n2,C=n3):\n" " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7]\n" ); -}; +} TEST(21_AntiParallelMOS3Transistors) { @@ -1024,7 +1024,7 @@ TEST(21_AntiParallelMOS3Transistors) "Circuit (A=n1,B=n2,C=n3):\n" " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7]\n" ); -}; +} TEST(22_ParallelMOS3TransistorsDisconnectedGates) { @@ -1093,7 +1093,7 @@ TEST(22_ParallelMOS3TransistorsDisconnectedGates) " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4]\n" ); -}; +} TEST(23_ParallelMOS3TransistorsDifferentLength) { @@ -1157,7 +1157,7 @@ TEST(23_ParallelMOS3TransistorsDifferentLength) " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4]\n" ); -}; +} TEST(30_ParallelMOS4Transistors) { @@ -1225,7 +1225,7 @@ TEST(30_ParallelMOS4Transistors) "Circuit (A=n1,B=n2,C=n3,D=n0):\n" " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7]\n" ); -}; +} TEST(31_AntiParallelMOS4Transistors) { @@ -1293,7 +1293,7 @@ TEST(31_AntiParallelMOS4Transistors) "Circuit (A=n1,B=n2,C=n3,D=n0):\n" " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7]\n" ); -}; +} TEST(32_ParallelMOS4TransistorsDisconnectedGates) { @@ -1369,7 +1369,7 @@ TEST(32_ParallelMOS4TransistorsDisconnectedGates) " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4]\n" ); -}; +} TEST(33_ParallelMOS4TransistorsDisconnectedBulk) { @@ -1445,7 +1445,7 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3]\n" " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4]\n" ); -}; +} TEST(34_ParallelMOS4TransistorsDifferentLength) { @@ -1516,5 +1516,5 @@ TEST(34_ParallelMOS4TransistorsDifferentLength) " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4]\n" ); -}; +} From 923e4075da9f135e1bdb1a84ae454975b7859625 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 31 Dec 2018 15:25:45 +0100 Subject: [PATCH 160/335] Added an intrinsic linked list implementation. --- src/tl/tl/tl.pro | 6 +- src/tl/tl/tlList.cc | 25 ++ src/tl/tl/tlList.h | 523 +++++++++++++++++++++++++++++++ src/tl/unit_tests/tlListTests.cc | 369 ++++++++++++++++++++++ src/tl/unit_tests/unit_tests.pro | 3 +- 5 files changed, 923 insertions(+), 3 deletions(-) create mode 100644 src/tl/tl/tlList.cc create mode 100644 src/tl/tl/tlList.h create mode 100644 src/tl/unit_tests/tlListTests.cc diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index 3805807d8..850bced12 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -42,7 +42,8 @@ SOURCES = \ tlDeferredExecution.cc \ tlUri.cc \ tlLongInt.cc \ - tlUniqueId.cc + tlUniqueId.cc \ + tlList.cc HEADERS = \ tlAlgorithm.h \ @@ -94,7 +95,8 @@ HEADERS = \ tlDeferredExecution.h \ tlUri.h \ tlLongInt.h \ - tlUniqueId.h + tlUniqueId.h \ + tlList.h equals(HAVE_CURL, "1") { diff --git a/src/tl/tl/tlList.cc b/src/tl/tl/tlList.cc new file mode 100644 index 000000000..fa9c84732 --- /dev/null +++ b/src/tl/tl/tlList.cc @@ -0,0 +1,25 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "tlList.h" + +// .. nothing yet .. diff --git a/src/tl/tl/tlList.h b/src/tl/tl/tlList.h new file mode 100644 index 000000000..226b89ff7 --- /dev/null +++ b/src/tl/tl/tlList.h @@ -0,0 +1,523 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_tlList +#define HDR_tlList + +#include "tlAssert.h" +#include "tlTypeTraits.h" + +#include + +namespace tl +{ + +template class list_impl; + +/** + * @brief A base class for objects that can be kept in the linked list + */ +template +class list_node +{ +public: + list_node () : mp_next (0), mp_prev (0), m_owned (true) { } + list_node (const list_node &) : mp_next (0), mp_prev (0), m_owned (true) { } + list_node &operator= (const list_node &) { } + + ~list_node () + { + unlink (); + } + + C *next () + { + tl_assert (mp_next); + return static_cast (mp_next->mp_next == 0 ? 0 : mp_next); + } + + const C *next () const + { + tl_assert (mp_next); + return static_cast (mp_next->mp_next == 0 ? 0 : mp_next); + } + + C *prev () + { + tl_assert (mp_prev); + return static_cast (mp_prev->mp_prev == 0 ? 0 : mp_prev); + } + + const C *prev () const + { + tl_assert (mp_prev); + return static_cast (mp_prev->mp_prev == 0 ? 0 : mp_prev); + } + + C *self () + { + return static_cast (this); + } + + const C *self () const + { + return static_cast (this); + } + + void unlink () + { + if (mp_prev) { + tl_assert (mp_prev->mp_next == this); + mp_prev->mp_next = mp_next; + } + if (mp_next) { + tl_assert (mp_next->mp_prev == this); + mp_next->mp_prev = mp_prev; + } + } + +private: + template friend class list_impl; + template friend class list; + template friend class list_iterator; + template friend class reverse_list_iterator; + + list_node *mp_next, *mp_prev; + bool m_owned; +}; + +template +class list_impl +{ +public: + list_impl () : m_head (), m_back () + { + m_head.mp_next = &m_back; + m_back.mp_prev = &m_head; + } + + list_impl (const list_impl &) { tl_assert (false); } + list_impl &operator= (const list_impl &) { tl_assert (false); } + + ~list_impl () + { + clear (); + } + + void clear () + { + while (! empty ()) { + if (first ()->m_owned) { + delete first (); + } else { + first ()->unlink (); + } + } + } + + bool empty () const + { + return m_head.mp_next == &m_back; + } + + C *first () + { + return ! empty () ? static_cast (m_head.mp_next) : 0; + } + + const C *first () const + { + return ! empty () ? static_cast (m_head.mp_next) : 0; + } + + C *last () + { + return ! empty () ? static_cast (m_back.mp_prev) : 0; + } + + const C *last () const + { + return ! empty () ? static_cast (m_back.mp_prev) : 0; + } + + void pop_back () + { + delete last (); + } + + void pop_front () + { + delete first (); + } + + void insert (C *after, C *new_obj) + { + insert_impl (after, new_obj, true); + } + + void insert_before (C *before, C *new_obj) + { + insert_before_impl (before, new_obj, true); + } + + void push_back (C *new_obj) + { + push_back_impl (new_obj, true); + } + + void push_front (C *new_obj) + { + push_front_impl (new_obj, true); + } + + void insert (C *after, C &new_obj) + { + insert_impl (after, new_obj, false); + } + + void insert_before (C *before, C &new_obj) + { + insert_before_impl (before, new_obj, false); + } + + void push_back (C &new_obj) + { + push_back_impl (new_obj, false); + } + + void push_front (C &new_obj) + { + push_front_impl (new_obj, false); + } + + size_t size () const + { + size_t n = 0; + for (const C *p = first (); p; p = p->next ()) { + ++n; + } + return n; + } + +protected: + list_node &head () + { + return m_head; + } + + const list_node &head () const + { + return m_head; + } + + list_node &back () + { + return m_back; + } + + const list_node &back () const + { + return m_back; + } + +private: + list_node m_head, m_back; + + void insert_impl (C *after, C *new_obj, bool owned) + { + list_node *after_node = after; + if (! after) { + after_node = &m_head; + } else { + after_node->m_owned = owned; + } + + new_obj->mp_next = after_node->mp_next; + after_node->mp_next = new_obj; + new_obj->mp_prev = after_node; + new_obj->mp_next->mp_prev = new_obj; + } + + void insert_before_impl (C *before, C *new_obj, bool owned) + { + list_node *before_node = before; + if (! before) { + before_node = &m_back; + } else { + before_node->m_owned = owned; + } + + new_obj->mp_prev = before_node->mp_prev; + before_node->mp_prev = new_obj; + new_obj->mp_next = before_node; + new_obj->mp_prev->mp_next = new_obj; + } + + void push_back_impl (C *new_obj, bool owned) + { + insert_before_impl (0, new_obj, owned); + } + + void push_front_impl (C *new_obj, bool owned) + { + insert_impl (0, new_obj, owned); + } +}; + +template +class list_impl + : public list_impl +{ +public: + using list_impl::insert; + using list_impl::push_back; + using list_impl::pop_back; + using list_impl::insert_before; + using list_impl::push_front; + using list_impl::pop_front; + + list_impl () { } + + list_impl (const list_impl &other) + { + operator= (other); + } + + list_impl &operator= (const list_impl &other) + { + if (this != &other) { + list_impl::clear (); + for (const C *p = other.first (); p; p = p->next ()) { + push_back (*p); + } + } + return *this; + } + + void insert (C *after, const C &obj) + { + insert (after, new C (obj)); + } + + void insert_before (C *before, const C &obj) + { + insert_before (before, new C (obj)); + } + + void push_back (const C &obj) + { + insert_before (0, new C (obj)); + } + + void push_front (const C &obj) + { + insert (0, new C (obj)); + } +}; + +/** + * @brief An iterator for the linked list + */ +template +class list_iterator +{ +public: + typedef std::bidirectional_iterator_tag category; + typedef C value_type; + typedef C &reference; + typedef C *pointer; + + list_iterator (C *p = 0) : mp_p (p) { } + list_iterator operator++ () { mp_p = static_cast (mp_p->mp_next); return *this; } + list_iterator operator-- () { mp_p = static_cast (mp_p->mp_prev); return *this; } + + C *operator-> () const + { + return mp_p; + } + + C &operator* () const + { + return *mp_p; + } + + bool operator== (list_iterator other) const { return mp_p == other.mp_p; } + bool operator!= (list_iterator other) const { return mp_p != other.mp_p; } + +private: + C *mp_p; +}; + +/** + * @brief A reverse iterator for the linked list + */ +template +class reverse_list_iterator +{ +public: + typedef std::bidirectional_iterator_tag category; + typedef C value_type; + typedef C &reference; + typedef C *pointer; + + reverse_list_iterator (C *p = 0) : mp_p (p) { } + reverse_list_iterator operator++ () { mp_p = static_cast (mp_p->mp_prev); return *this; } + reverse_list_iterator operator-- () { mp_p = static_cast (mp_p->mp_next); return *this; } + + C *operator-> () const + { + return mp_p; + } + + C &operator* () const + { + return *mp_p; + } + + bool operator== (reverse_list_iterator other) const { return mp_p == other.mp_p; } + bool operator!= (reverse_list_iterator other) const { return mp_p != other.mp_p; } + +private: + C *mp_p; +}; + +/** + * @brief A linked list + * + * In contrast to std::list this implementation is based on derivation from + * a common base class (list_node where C is the type that needs to be + * put into the list. + * + * The advantage of this approach is that the elements can unregister them + * selves upon delete, there are no iterators involved in insert and delete + * operations and each object knows it's followers and predecessors. + * + * @code + * class MyClass : public tl::list_node { ... }; + * + * tl::list list; + * list.push_back (new MyClass ()); + */ +template +class list + : public list_impl::has_copy_constructor> +{ +public: + typedef list_iterator iterator; + typedef list_iterator const_iterator; + typedef reverse_list_iterator reverse_iterator; + typedef reverse_list_iterator const_reverse_iterator; + + typedef C value_type; + + using list_impl::has_copy_constructor>::first; + using list_impl::has_copy_constructor>::last; + using list_impl::has_copy_constructor>::head; + using list_impl::has_copy_constructor>::back; + + list () { } + list (const list &other) : list_impl::has_copy_constructor> (other) { } + + list &operator= (const list &other) + { + list_impl::has_copy_constructor>::operator= (other); + return *this; + } + + iterator begin () + { + return iterator (static_cast (head ().mp_next)); + } + + iterator end () + { + return iterator (static_cast (&back ())); + } + + const_iterator begin () const + { + return const_iterator (static_cast (head ().mp_next)); + } + + const_iterator end () const + { + return const_iterator (static_cast (&back ())); + } + + reverse_iterator rbegin () + { + return reverse_iterator (static_cast (back ().mp_prev)); + } + + reverse_iterator rend () + { + return reverse_iterator (static_cast (&head ())); + } + + const_reverse_iterator rbegin () const + { + return const_reverse_iterator (static_cast (back ().mp_prev)); + } + + const_reverse_iterator rend () const + { + return const_reverse_iterator (static_cast (&head ())); + } + + bool operator== (const list &other) const + { + const C *i1 = first (); + const C *i2 = other.first (); + while (i1 && i2) { + if (! (*i1 == *i2)) { + return false; + } + i1 = i1->next (); + i2 = i2->next (); + } + return (i1 == 0 && i2 == 0); + } + + bool operator!= (const list &other) const + { + return !operator== (other); + } + + bool operator< (const list &other) const + { + const C *i1 = first (); + const C *i2 = other.first (); + while (i1 && i2) { + if (! (*i1 == *i2)) { + return *i1 < *i2; + } + i1 = i1->next (); + i2 = i2->next (); + } + return ((i1 == 0) > (i2 == 0)); + } +}; + +}; + +#endif diff --git a/src/tl/unit_tests/tlListTests.cc b/src/tl/unit_tests/tlListTests.cc new file mode 100644 index 000000000..f098aaa7c --- /dev/null +++ b/src/tl/unit_tests/tlListTests.cc @@ -0,0 +1,369 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "tlList.h" +#include "tlUnitTest.h" +#include "tlString.h" + +namespace +{ + +struct MyClass1 : public tl::list_node +{ + MyClass1 (int _n) : n (_n) { } + int n; + bool operator== (const MyClass1 &other) const { return n == other.n; } + bool operator< (const MyClass1 &other) const { return n < other.n; } +}; + +struct MyClass2 : public tl::list_node +{ + MyClass2 (int _n) : n (_n) { } + int n; +public: + MyClass2 (const MyClass2 &other); + MyClass2 &operator= (const MyClass2 &other); + bool operator== (const MyClass2 &other) const { return n == other.n; } + bool operator< (const MyClass2 &other) const { return n < other.n; } +}; + +} + +namespace tl +{ + template <> + struct type_traits : public tl::type_traits + { + typedef tl::false_tag has_copy_constructor; + }; +} + +template +static std::string l2s (const tl::list &l) +{ + std::string x; + for (typename tl::list::const_iterator i = l.begin (); i != l.end (); ++i) { + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2sr (const tl::list &l) +{ + std::string x; + for (typename tl::list::const_reverse_iterator i = l.rbegin (); i != l.rend (); ++i) { + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2sm (const tl::list &l) +{ + std::string x; + for (typename tl::list::const_iterator i = l.end (); i != l.begin (); ) { + --i; + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2srm (const tl::list &l) +{ + std::string x; + for (typename tl::list::const_reverse_iterator i = l.rend (); i != l.rbegin (); ) { + --i; + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2s_nc (tl::list &l) +{ + std::string x; + for (typename tl::list::iterator i = l.begin (); i != l.end (); ++i) { + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2sr_nc (tl::list &l) +{ + std::string x; + for (typename tl::list::reverse_iterator i = l.rbegin (); i != l.rend (); ++i) { + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2sm_nc (tl::list &l) +{ + std::string x; + for (typename tl::list::iterator i = l.end (); i != l.begin (); ) { + --i; + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2srm_nc (tl::list &l) +{ + std::string x; + for (typename tl::list::reverse_iterator i = l.rend (); i != l.rbegin (); ) { + --i; + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +TEST(1_Basic) +{ + tl::list l1, l2; + + EXPECT_EQ (l1.empty (), true); + EXPECT_EQ (l1.size (), size_t (0)); + EXPECT_EQ (l2s (l1), ""); + EXPECT_EQ (l2sr (l1), ""); + + l1.push_back (new MyClass1 (17)); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (1)); + EXPECT_EQ (l2s (l1), "17"); + EXPECT_EQ (l2sr (l1), "17"); + + l1.push_back (MyClass1 (42)); + l2 = l1; + tl::list l3 (l2); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (2)); + EXPECT_EQ (l2s (l1), "17,42"); + EXPECT_EQ (l2sr (l1), "42,17"); + + delete l1.first (); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (1)); + EXPECT_EQ (l2s (l1), "42"); + EXPECT_EQ (l2sr (l1), "42"); + + l1.clear (); + EXPECT_EQ (l1.empty (), true); + EXPECT_EQ (l1.size (), size_t (0)); + EXPECT_EQ (l2s (l1), ""); + EXPECT_EQ (l2sr (l1), ""); + + EXPECT_EQ (l2s (l2), "17,42"); + EXPECT_EQ (l2sr (l2), "42,17"); + l2.pop_back (); + EXPECT_EQ (l2s (l2), "17"); + EXPECT_EQ (l2sr (l2), "17"); + + l3.push_back (new MyClass1 (2)); + l3.push_front (new MyClass1 (1)); + EXPECT_EQ (l2s (l3), "1,17,42,2"); + EXPECT_EQ (l2srm (l3), "1,17,42,2"); + EXPECT_EQ (l2sm (l3), "2,42,17,1"); + EXPECT_EQ (l2sr (l3), "2,42,17,1"); + EXPECT_EQ (l2s_nc (l3), "1,17,42,2"); + EXPECT_EQ (l2srm_nc (l3), "1,17,42,2"); + EXPECT_EQ (l2sm_nc (l3), "2,42,17,1"); + EXPECT_EQ (l2sr_nc (l3), "2,42,17,1"); + EXPECT_EQ (l3.size (), size_t (4)); + + l3.pop_back (); + EXPECT_EQ (l2s (l3), "1,17,42"); + EXPECT_EQ (l2sr (l3), "42,17,1"); + EXPECT_EQ (l3.size (), size_t (3)); + + MyClass1 *c1; + c1 = l3.first (); + EXPECT_EQ (c1->n, 1); + c1 = c1->next (); + EXPECT_EQ (c1->n, 17); + c1 = c1->next (); + EXPECT_EQ (c1->n, 42); + EXPECT_EQ (c1->next (), 0); + + c1 = l3.last (); + EXPECT_EQ (c1->n, 42); + c1 = c1->prev (); + EXPECT_EQ (c1->n, 17); + c1 = c1->prev (); + EXPECT_EQ (c1->n, 1); + EXPECT_EQ (c1->prev (), 0); + + l3.pop_front (); + EXPECT_EQ (l2s (l3), "17,42"); + EXPECT_EQ (l2sr (l3), "42,17"); + EXPECT_EQ (l3.size (), size_t (2)); + + l3.push_back (new MyClass1 (1)); + EXPECT_EQ (l2s (l3), "17,42,1"); + EXPECT_EQ (l2sr (l3), "1,42,17"); + EXPECT_EQ (l3.size (), size_t (3)); + + c1 = l3.first ()->next (); + delete c1; + EXPECT_EQ (l2s (l3), "17,1"); + EXPECT_EQ (l2sr (l3), "1,17"); + EXPECT_EQ (l3.size (), size_t (2)); +} + +TEST(2_BasicNoCopy) +{ + tl::list l1, l2, l3; + + EXPECT_EQ (l1.empty (), true); + EXPECT_EQ (l1.size (), size_t (0)); + EXPECT_EQ (l2s (l1), ""); + EXPECT_EQ (l2sr (l1), ""); + + l1.push_back (new MyClass2 (17)); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (1)); + EXPECT_EQ (l2s (l1), "17"); + EXPECT_EQ (l2sr (l1), "17"); + + l1.push_back (new MyClass2 (42)); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (2)); + EXPECT_EQ (l2s (l1), "17,42"); + EXPECT_EQ (l2sr (l1), "42,17"); + + delete l1.first (); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (1)); + EXPECT_EQ (l2s (l1), "42"); + EXPECT_EQ (l2sr (l1), "42"); + + l1.clear (); + EXPECT_EQ (l1.empty (), true); + EXPECT_EQ (l1.size (), size_t (0)); + EXPECT_EQ (l2s (l1), ""); + EXPECT_EQ (l2sr (l1), ""); + + l2.push_back (new MyClass2 (17)); + l2.push_back (new MyClass2 (42)); + + EXPECT_EQ (l2s (l2), "17,42"); + EXPECT_EQ (l2sr (l2), "42,17"); + l2.pop_back (); + EXPECT_EQ (l2s (l2), "17"); + EXPECT_EQ (l2sr (l2), "17"); + + EXPECT_EQ (l2 == l3, false); + EXPECT_EQ (l2 != l3, true); + EXPECT_EQ (l2 < l3, false); + + l3.push_back (new MyClass2 (17)); + EXPECT_EQ (l2 == l3, true); + EXPECT_EQ (l2 != l3, false); + EXPECT_EQ (l2 < l3, false); + + l3.push_back (new MyClass2 (42)); + EXPECT_EQ (l2 == l3, false); + EXPECT_EQ (l2 != l3, true); + EXPECT_EQ (l2 < l3, true); + + l3.push_back (new MyClass2 (2)); + l3.push_front (new MyClass2 (1)); + EXPECT_EQ (l2 == l3, false); + EXPECT_EQ (l2 != l3, true); + EXPECT_EQ (l2 < l3, false); + + EXPECT_EQ (l2s (l3), "1,17,42,2"); + EXPECT_EQ (l2srm (l3), "1,17,42,2"); + EXPECT_EQ (l2sm (l3), "2,42,17,1"); + EXPECT_EQ (l2sr (l3), "2,42,17,1"); + EXPECT_EQ (l2s_nc (l3), "1,17,42,2"); + EXPECT_EQ (l2srm_nc (l3), "1,17,42,2"); + EXPECT_EQ (l2sm_nc (l3), "2,42,17,1"); + EXPECT_EQ (l2sr_nc (l3), "2,42,17,1"); + EXPECT_EQ (l3.size (), size_t (4)); + + l3.pop_back (); + EXPECT_EQ (l2s (l3), "1,17,42"); + EXPECT_EQ (l2sr (l3), "42,17,1"); + EXPECT_EQ (l3.size (), size_t (3)); + + MyClass2 *c1; + c1 = l3.first (); + EXPECT_EQ (c1->n, 1); + c1 = c1->next (); + EXPECT_EQ (c1->n, 17); + c1 = c1->next (); + EXPECT_EQ (c1->n, 42); + EXPECT_EQ (c1->next (), 0); + + c1 = l3.last (); + EXPECT_EQ (c1->n, 42); + c1 = c1->prev (); + EXPECT_EQ (c1->n, 17); + c1 = c1->prev (); + EXPECT_EQ (c1->n, 1); + EXPECT_EQ (c1->prev (), 0); + + l3.pop_front (); + EXPECT_EQ (l2s (l3), "17,42"); + EXPECT_EQ (l2sr (l3), "42,17"); + EXPECT_EQ (l3.size (), size_t (2)); + + l3.push_back (new MyClass2 (1)); + EXPECT_EQ (l2s (l3), "17,42,1"); + EXPECT_EQ (l2sr (l3), "1,42,17"); + EXPECT_EQ (l3.size (), size_t (3)); + + c1 = l3.first ()->next (); + delete c1; + EXPECT_EQ (l2s (l3), "17,1"); + EXPECT_EQ (l2sr (l3), "1,17"); + EXPECT_EQ (l3.size (), size_t (2)); +} diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index f10fc4e70..bd1680b9f 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -35,7 +35,8 @@ SOURCES = \ tlHttpStream.cc \ tlInt128Support.cc \ tlLongInt.cc \ - tlUniqueIdTests.cc + tlUniqueIdTests.cc \ + tlListTests.cc !equals(HAVE_QT, "0") { From 37d8f0bfca80c9a923aa454269eeb63e858baa5d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 31 Dec 2018 16:41:32 +0100 Subject: [PATCH 161/335] Added hierarchical progress reporting for more detailed progress. --- src/db/db/dbHierNetworkProcessor.cc | 17 ++- src/lay/lay/layMainWindow.cc | 59 ++------ src/lay/lay/layMainWindow.h | 16 +-- src/lay/lay/layProgress.cc | 46 +++---- src/lay/lay/layProgress.h | 6 +- src/lay/lay/layTextProgress.cc | 16 +-- src/lay/lay/layTextProgress.h | 4 +- src/tl/tl/tlList.h | 30 ++-- src/tl/tl/tlProgress.cc | 33 ++++- src/tl/tl/tlProgress.h | 23 ++++ src/tl/unit_tests/tlListTests.cc | 206 ++++++++++++++++------------ 11 files changed, 241 insertions(+), 215 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index eadc2b973..7f6cc1289 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -535,7 +535,10 @@ template void local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) { - db::box_scanner > bs; + bool report_progress = tl::verbosity () >= 50; + static std::string desc = tl::to_string (tr ("Building local clusters")); + + db::box_scanner > bs (report_progress, desc); typename T::tag object_tag; db::box_convert bc; @@ -1306,9 +1309,11 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // handle instance to instance connections { - tl::SelfTimer timer (tl::verbosity () >= 51, tl::to_string (tr ("Instance to instance treatment"))); + static std::string desc = tl::to_string (tr ("Instance to instance treatment")); + tl::SelfTimer timer (tl::verbosity () >= 51, desc); - db::box_scanner bs; + bool report_progress = tl::verbosity () >= 50; + db::box_scanner bs (report_progress, desc); for (std::vector::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) { bs.insert (inst.operator-> (), 0); @@ -1320,9 +1325,11 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // handle local to instance connections { - tl::SelfTimer timer (tl::verbosity () >= 51, tl::to_string (tr ("Local to instance treatment"))); + static std::string desc = tl::to_string (tr ("Local to instance treatment")); + tl::SelfTimer timer (tl::verbosity () >= 51, desc); - db::box_scanner2, unsigned int, db::Instance, unsigned int> bs2; + bool report_progress = tl::verbosity () >= 50; + db::box_scanner2, unsigned int, db::Instance, unsigned int> bs2 (report_progress, desc); for (typename connected_clusters::const_iterator c = local.begin (); c != local.end (); ++c) { bs2.insert1 (c.operator-> (), 0); diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index d988c1a33..352f6e504 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -379,24 +379,10 @@ TextProgressDelegate::TextProgressDelegate (MainWindow *mw, int verbosity) // .. nothing yet .. } -void TextProgressDelegate::set_progress_can_cancel (bool f) +void TextProgressDelegate::update_progress (tl::Progress *progress) { - if (!mp_mw->set_progress_can_cancel (f)) { - lay::TextProgress::set_progress_can_cancel (f); - } -} - -void TextProgressDelegate::set_progress_text (const std::string &text) -{ - if (!mp_mw->set_progress_text (text)) { - lay::TextProgress::set_progress_text (text); - } -} - -void TextProgressDelegate::set_progress_value (double v, const std::string &value) -{ - if (!mp_mw->set_progress_value (v, value)) { - lay::TextProgress::set_progress_value (v, value); + if (!mp_mw->update_progress (progress)) { + lay::TextProgress::update_progress (progress); } } @@ -4642,42 +4628,27 @@ MainWindow::progress_get_widget () const } bool -MainWindow::set_progress_can_cancel (bool f) +MainWindow::update_progress (tl::Progress *progress) { - if (mp_progress_dialog) { - mp_progress_dialog->set_can_cancel (f); - return true; - } else if (isVisible () && mp_progress_widget) { - mp_progress_widget->set_can_cancel (f); - return true; - } else { - return false; - } -} + bool can_cancel = progress->can_cancel (); + std::string text = progress->desc (); + std::string value = progress->formatted_value (); + double v = progress->value (); -bool -MainWindow::set_progress_text (const std::string &text) -{ if (mp_progress_dialog) { + + mp_progress_dialog->set_can_cancel (can_cancel); mp_progress_dialog->set_text (text); - return true; - } else if (isVisible () && mp_progress_widget) { - mp_progress_widget->set_text (text); - return true; - } else { - return false; - } -} - -bool -MainWindow::set_progress_value (double v, const std::string &value) -{ - if (mp_progress_dialog) { mp_progress_dialog->set_value (v, value); return true; + } else if (isVisible () && mp_progress_widget) { + + mp_progress_widget->set_can_cancel (can_cancel); + mp_progress_widget->set_text (text); mp_progress_widget->set_value (v, value); return true; + } else { return false; } diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index cc3b21bf4..8bb84bd04 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -105,9 +105,7 @@ class TextProgressDelegate public: TextProgressDelegate (MainWindow *mw, int verbosity); - virtual void set_progress_can_cancel (bool f); - virtual void set_progress_text (const std::string &text); - virtual void set_progress_value (double v, const std::string &value); + virtual void update_progress (tl::Progress *progress); virtual void show_progress_bar (bool show); virtual bool progress_wants_widget () const; virtual void progress_add_widget (QWidget *widget); @@ -381,17 +379,7 @@ public: /** * @brief Implementation of the lay::ProgressBar interface: set the flag indicating whether we can cancel the operation */ - bool set_progress_can_cancel(bool f); - - /** - * @brief Implementation of the lay::ProgressBar interface: set the text to display - */ - bool set_progress_text (const std::string &text); - - /** - * @brief Implementation of the lay::ProgressBar interface: set the value to display - */ - bool set_progress_value (double v, const std::string &value); + bool update_progress (tl::Progress *progress); /** * @brief Implementation of the lay::ProgressBar interface: Returns a value indicating whether a progress widget is wanted diff --git a/src/lay/lay/layProgress.cc b/src/lay/lay/layProgress.cc index daefbaff1..1a0af1d0a 100644 --- a/src/lay/lay/layProgress.cc +++ b/src/lay/lay/layProgress.cc @@ -92,7 +92,7 @@ ProgressReporter::register_object (tl::Progress *progress) QApplication::instance ()->installEventFilter (this); } - mp_objects.push_back (progress); // this keeps the outmost one visible. push_front would make the latest one visible. + mp_objects.push_back (*progress); // this keeps the outmost one visible. push_front would make the latest one visible. // mp_objects.push_front (progress); if (m_start_time == tl::Clock () && ! m_pw_visible) { @@ -110,36 +110,27 @@ ProgressReporter::register_object (tl::Progress *progress) void ProgressReporter::unregister_object (tl::Progress *progress) { - for (std::list ::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { - - if (*k == progress) { - - mp_objects.erase (k); - - // close or refresh window - if (mp_objects.empty ()) { - if (m_pw_visible) { - set_visible (false); - } - m_start_time = tl::Clock (); - } - - update_and_yield (); - break; + progress->unlink (); + // close or refresh window + if (mp_objects.empty ()) { + if (m_pw_visible) { + set_visible (false); } - + m_start_time = tl::Clock (); } + update_and_yield (); + if (mp_objects.empty ()) { QApplication::instance ()->removeEventFilter (this); } } void -ProgressReporter::trigger (tl::Progress *progress) +ProgressReporter::trigger (tl::Progress * /*progress*/) { - if (! mp_objects.empty () && mp_objects.front () == progress) { + if (! mp_objects.empty ()) { // make dialog visible after some time has passed if (! m_pw_visible && (tl::Clock::current () - m_start_time).seconds () > 1.0) { set_visible (true); @@ -164,8 +155,8 @@ ProgressReporter::yield (tl::Progress * /*progress*/) void ProgressReporter::signal_break () { - for (std::list ::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { - (*k)->signal_break (); + for (tl::list::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { + k->signal_break (); } } @@ -174,13 +165,10 @@ ProgressReporter::update_and_yield () { if (m_pw_visible && ! mp_objects.empty ()) { if (mp_pb) { - // not supported yet: mp_pb->progress_widget ()->set_title ((*mp_objects.begin ())->title ()); - mp_pb->set_progress_can_cancel (mp_objects.front ()->can_cancel ()); - mp_pb->set_progress_text (mp_objects.front ()->desc ()); - mp_pb->set_progress_value (mp_objects.front ()->value (), mp_objects.front ()->formatted_value ()); + mp_pb->update_progress (mp_objects.first ()); QWidget *w = mp_pb->progress_get_widget (); if (w) { - mp_objects.front ()->render_progress (w); + mp_objects.first ()->render_progress (w); } } process_events (); // Qt4 seems to need this @@ -214,8 +202,8 @@ ProgressReporter::set_visible (bool vis) if (mp_pb) { if (!vis) { mp_pb->progress_remove_widget (); - } else if (mp_pb->progress_wants_widget () && mp_objects.front ()) { - mp_pb->progress_add_widget (mp_objects.front ()->progress_widget ()); + } else if (mp_pb->progress_wants_widget () && mp_objects.first ()) { + mp_pb->progress_add_widget (mp_objects.first ()->progress_widget ()); } } diff --git a/src/lay/lay/layProgress.h b/src/lay/lay/layProgress.h index dcaa5e405..a84549535 100644 --- a/src/lay/lay/layProgress.h +++ b/src/lay/lay/layProgress.h @@ -47,9 +47,7 @@ class LAY_PUBLIC ProgressBar public: virtual ~ProgressBar () { } - virtual void set_progress_can_cancel (bool f) = 0; - virtual void set_progress_text (const std::string &text) = 0; - virtual void set_progress_value (double v, const std::string &value) = 0; + virtual void update_progress (tl::Progress *progress) = 0; virtual bool progress_wants_widget () const { return false; } virtual void progress_add_widget (QWidget * /*widget*/) { } virtual void progress_remove_widget () { } @@ -79,7 +77,7 @@ public: } private: - std::list mp_objects; + tl::list mp_objects; tl::Clock m_start_time; lay::ProgressBar *mp_pb; bool m_pw_visible; diff --git a/src/lay/lay/layTextProgress.cc b/src/lay/lay/layTextProgress.cc index 604509dae..a466b933c 100644 --- a/src/lay/lay/layTextProgress.cc +++ b/src/lay/lay/layTextProgress.cc @@ -33,21 +33,19 @@ TextProgress::TextProgress (int verbosity) // .. nothing yet .. } -void TextProgress::set_progress_can_cancel (bool /*f*/) -{ - // .. nothing yet .. -} - -void TextProgress::set_progress_text (const std::string &text) +void TextProgress::update_progress (tl::Progress *progress) { + std::string text = progress->desc (); if (m_progress_text != text && tl::verbosity () >= m_verbosity) { tl::info << text << " .."; m_progress_text = text; } -} -void TextProgress::set_progress_value (double /*value*/, const std::string &value) -{ + std::string value = progress->formatted_value (); + for (tl::Progress *p = progress->next (); p != 0; p = p->next ()) { + value += " " + p->formatted_value (); + } + if (m_progress_value != value && tl::verbosity () >= m_verbosity) { tl::info << ".. " << value; m_progress_value = value; diff --git a/src/lay/lay/layTextProgress.h b/src/lay/lay/layTextProgress.h index fef73db26..23a0638b7 100644 --- a/src/lay/lay/layTextProgress.h +++ b/src/lay/lay/layTextProgress.h @@ -46,9 +46,7 @@ public: */ TextProgress (int verbosity); - virtual void set_progress_can_cancel (bool f); - virtual void set_progress_text (const std::string &text); - virtual void set_progress_value (double v, const std::string &value); + virtual void update_progress (tl::Progress *progress); virtual void show_progress_bar (bool show); private: diff --git a/src/tl/tl/tlList.h b/src/tl/tl/tlList.h index 226b89ff7..ff66cb359 100644 --- a/src/tl/tl/tlList.h +++ b/src/tl/tl/tlList.h @@ -94,6 +94,7 @@ public: tl_assert (mp_next->mp_prev == this); mp_next->mp_prev = mp_prev; } + mp_prev = mp_next = 0; } private: @@ -127,11 +128,16 @@ public: void clear () { while (! empty ()) { - if (first ()->m_owned) { - delete first (); - } else { - first ()->unlink (); - } + erase (first ()); + } + } + + void erase (C *c) + { + if (c->m_owned) { + delete c; + } else { + c->unlink (); } } @@ -192,22 +198,22 @@ public: void insert (C *after, C &new_obj) { - insert_impl (after, new_obj, false); + insert_impl (after, &new_obj, false); } void insert_before (C *before, C &new_obj) { - insert_before_impl (before, new_obj, false); + insert_before_impl (before, &new_obj, false); } void push_back (C &new_obj) { - push_back_impl (new_obj, false); + push_back_impl (&new_obj, false); } void push_front (C &new_obj) { - push_front_impl (new_obj, false); + push_front_impl (&new_obj, false); } size_t size () const @@ -248,10 +254,9 @@ private: list_node *after_node = after; if (! after) { after_node = &m_head; - } else { - after_node->m_owned = owned; } + new_obj->m_owned = owned; new_obj->mp_next = after_node->mp_next; after_node->mp_next = new_obj; new_obj->mp_prev = after_node; @@ -263,10 +268,9 @@ private: list_node *before_node = before; if (! before) { before_node = &m_back; - } else { - before_node->m_owned = owned; } + new_obj->m_owned = owned; new_obj->mp_prev = before_node->mp_prev; before_node->mp_prev = new_obj; new_obj->mp_next = before_node; diff --git a/src/tl/tl/tlProgress.cc b/src/tl/tl/tlProgress.cc index a1501caa9..84df5ea01 100644 --- a/src/tl/tl/tlProgress.cc +++ b/src/tl/tl/tlProgress.cc @@ -72,6 +72,17 @@ Progress::Progress (const std::string &desc, size_t yield_interval) m_last_value (-1.0), m_can_cancel (true), m_cancelled (false) +{ + // .. nothing yet .. +} + +Progress::~Progress () +{ + // .. nothing yet .. +} + +void +Progress::initialize () { ProgressAdaptor *a = adaptor (); if (a) { @@ -79,7 +90,8 @@ Progress::Progress (const std::string &desc, size_t yield_interval) } } -Progress::~Progress () +void +Progress::shutdown () { ProgressAdaptor *a = adaptor (); if (a) { @@ -134,7 +146,6 @@ Progress::set_desc (const std::string &d) } } - } void @@ -181,6 +192,14 @@ RelativeProgress::RelativeProgress (const std::string &desc, size_t max_count, s m_format = "%.0f%%"; m_unit = double (max_count) / 100.0; m_count = 0; + m_last_count = 0; + + initialize (); +} + +RelativeProgress::~RelativeProgress () +{ + shutdown (); } double @@ -203,7 +222,8 @@ RelativeProgress & RelativeProgress::set (size_t count, bool force_yield) { m_count = count; - test (force_yield); + test (force_yield || m_count - m_last_count >= m_unit); + m_last_count = m_count; return *this; } @@ -217,6 +237,13 @@ AbsoluteProgress::AbsoluteProgress (const std::string &desc, size_t yield_interv m_unit = 1.0; m_format_unit = 0.0; m_count = 0; + + initialize (); +} + +AbsoluteProgress::~AbsoluteProgress () +{ + shutdown (); } double diff --git a/src/tl/tl/tlProgress.h b/src/tl/tl/tlProgress.h index aa79e4e86..1f41abb43 100644 --- a/src/tl/tl/tlProgress.h +++ b/src/tl/tl/tlProgress.h @@ -29,6 +29,7 @@ #include #include "tlException.h" #include "tlTimer.h" +#include "tlList.h" class QWidget; @@ -105,6 +106,7 @@ class Progress; */ class TL_PUBLIC Progress + : public tl::list_node { public: /** @@ -203,6 +205,16 @@ protected: */ void test (bool force_yield = false); + /** + * @brief This method needs to be called by all derived classes after initialization has happened + */ + void initialize (); + + /** + * @brief This method needs to be called by all derived classes in the destructor + */ + void shutdown (); + private: friend class ProgressAdaptor; @@ -241,6 +253,11 @@ public: */ RelativeProgress (const std::string &desc, size_t max_count = 0, size_t yield_interval = 1000); + /** + * @brief Destructor + */ + ~RelativeProgress (); + /** * @brief Delivers the current progress as a string */ @@ -283,6 +300,7 @@ private: std::string m_format; size_t m_count; + size_t m_last_count; double m_unit; }; @@ -309,6 +327,11 @@ public: */ AbsoluteProgress (const std::string &desc, size_t yield_interval = 1000); + /** + * @brief Destructor + */ + ~AbsoluteProgress (); + /** * @brief Delivers the current progress as a string */ diff --git a/src/tl/unit_tests/tlListTests.cc b/src/tl/unit_tests/tlListTests.cc index f098aaa7c..5fde89b69 100644 --- a/src/tl/unit_tests/tlListTests.cc +++ b/src/tl/unit_tests/tlListTests.cc @@ -27,9 +27,13 @@ namespace { +static size_t obj_count = 0; + struct MyClass1 : public tl::list_node { - MyClass1 (int _n) : n (_n) { } + MyClass1 (int _n) : n (_n) { ++obj_count; } + MyClass1 (const MyClass1 &other) : n (other.n) { ++obj_count; } + ~MyClass1 () { --obj_count; } int n; bool operator== (const MyClass1 &other) const { return n == other.n; } bool operator< (const MyClass1 &other) const { return n < other.n; } @@ -37,7 +41,8 @@ struct MyClass1 : public tl::list_node struct MyClass2 : public tl::list_node { - MyClass2 (int _n) : n (_n) { } + MyClass2 (int _n) : n (_n) { ++obj_count; } + ~MyClass2 () { --obj_count; } int n; public: MyClass2 (const MyClass2 &other); @@ -167,6 +172,8 @@ static std::string l2srm_nc (tl::list &l) TEST(1_Basic) { + obj_count = 0; + tl::list l1, l2; EXPECT_EQ (l1.empty (), true); @@ -255,115 +262,132 @@ TEST(1_Basic) EXPECT_EQ (l2s (l3), "17,1"); EXPECT_EQ (l2sr (l3), "1,17"); EXPECT_EQ (l3.size (), size_t (2)); + + l1.clear (); + l2.clear (); + l3.clear (); + EXPECT_EQ (obj_count, size_t (0)); } TEST(2_BasicNoCopy) { - tl::list l1, l2, l3; + { + obj_count = 0; - EXPECT_EQ (l1.empty (), true); - EXPECT_EQ (l1.size (), size_t (0)); - EXPECT_EQ (l2s (l1), ""); - EXPECT_EQ (l2sr (l1), ""); + MyClass2 mc2 (42); // will not be owned + tl::list l1, l2, l3; - l1.push_back (new MyClass2 (17)); - EXPECT_EQ (l1.empty (), false); - EXPECT_EQ (l1.size (), size_t (1)); - EXPECT_EQ (l2s (l1), "17"); - EXPECT_EQ (l2sr (l1), "17"); + EXPECT_EQ (l1.empty (), true); + EXPECT_EQ (l1.size (), size_t (0)); + EXPECT_EQ (l2s (l1), ""); + EXPECT_EQ (l2sr (l1), ""); - l1.push_back (new MyClass2 (42)); - EXPECT_EQ (l1.empty (), false); - EXPECT_EQ (l1.size (), size_t (2)); - EXPECT_EQ (l2s (l1), "17,42"); - EXPECT_EQ (l2sr (l1), "42,17"); + l1.push_back (new MyClass2 (17)); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (1)); + EXPECT_EQ (l2s (l1), "17"); + EXPECT_EQ (l2sr (l1), "17"); - delete l1.first (); - EXPECT_EQ (l1.empty (), false); - EXPECT_EQ (l1.size (), size_t (1)); - EXPECT_EQ (l2s (l1), "42"); - EXPECT_EQ (l2sr (l1), "42"); + l1.push_back (mc2); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (2)); + EXPECT_EQ (l2s (l1), "17,42"); + EXPECT_EQ (l2sr (l1), "42,17"); - l1.clear (); - EXPECT_EQ (l1.empty (), true); - EXPECT_EQ (l1.size (), size_t (0)); - EXPECT_EQ (l2s (l1), ""); - EXPECT_EQ (l2sr (l1), ""); + delete l1.first (); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (1)); + EXPECT_EQ (l2s (l1), "42"); + EXPECT_EQ (l2sr (l1), "42"); - l2.push_back (new MyClass2 (17)); - l2.push_back (new MyClass2 (42)); + l1.clear (); + EXPECT_EQ (l1.empty (), true); + EXPECT_EQ (l1.size (), size_t (0)); + EXPECT_EQ (l2s (l1), ""); + EXPECT_EQ (l2sr (l1), ""); - EXPECT_EQ (l2s (l2), "17,42"); - EXPECT_EQ (l2sr (l2), "42,17"); - l2.pop_back (); - EXPECT_EQ (l2s (l2), "17"); - EXPECT_EQ (l2sr (l2), "17"); + l2.push_back (new MyClass2 (17)); + l2.push_back (new MyClass2 (42)); - EXPECT_EQ (l2 == l3, false); - EXPECT_EQ (l2 != l3, true); - EXPECT_EQ (l2 < l3, false); + EXPECT_EQ (l2s (l2), "17,42"); + EXPECT_EQ (l2sr (l2), "42,17"); + l2.pop_back (); + EXPECT_EQ (l2s (l2), "17"); + EXPECT_EQ (l2sr (l2), "17"); - l3.push_back (new MyClass2 (17)); - EXPECT_EQ (l2 == l3, true); - EXPECT_EQ (l2 != l3, false); - EXPECT_EQ (l2 < l3, false); + EXPECT_EQ (l2 == l3, false); + EXPECT_EQ (l2 != l3, true); + EXPECT_EQ (l2 < l3, false); - l3.push_back (new MyClass2 (42)); - EXPECT_EQ (l2 == l3, false); - EXPECT_EQ (l2 != l3, true); - EXPECT_EQ (l2 < l3, true); + l3.push_back (new MyClass2 (17)); + EXPECT_EQ (l2 == l3, true); + EXPECT_EQ (l2 != l3, false); + EXPECT_EQ (l2 < l3, false); - l3.push_back (new MyClass2 (2)); - l3.push_front (new MyClass2 (1)); - EXPECT_EQ (l2 == l3, false); - EXPECT_EQ (l2 != l3, true); - EXPECT_EQ (l2 < l3, false); + l3.push_back (new MyClass2 (42)); + EXPECT_EQ (l2 == l3, false); + EXPECT_EQ (l2 != l3, true); + EXPECT_EQ (l2 < l3, true); - EXPECT_EQ (l2s (l3), "1,17,42,2"); - EXPECT_EQ (l2srm (l3), "1,17,42,2"); - EXPECT_EQ (l2sm (l3), "2,42,17,1"); - EXPECT_EQ (l2sr (l3), "2,42,17,1"); - EXPECT_EQ (l2s_nc (l3), "1,17,42,2"); - EXPECT_EQ (l2srm_nc (l3), "1,17,42,2"); - EXPECT_EQ (l2sm_nc (l3), "2,42,17,1"); - EXPECT_EQ (l2sr_nc (l3), "2,42,17,1"); - EXPECT_EQ (l3.size (), size_t (4)); + l3.push_back (new MyClass2 (2)); + l3.push_front (new MyClass2 (1)); + EXPECT_EQ (l2 == l3, false); + EXPECT_EQ (l2 != l3, true); + EXPECT_EQ (l2 < l3, false); - l3.pop_back (); - EXPECT_EQ (l2s (l3), "1,17,42"); - EXPECT_EQ (l2sr (l3), "42,17,1"); - EXPECT_EQ (l3.size (), size_t (3)); + EXPECT_EQ (l2s (l3), "1,17,42,2"); + EXPECT_EQ (l2srm (l3), "1,17,42,2"); + EXPECT_EQ (l2sm (l3), "2,42,17,1"); + EXPECT_EQ (l2sr (l3), "2,42,17,1"); + EXPECT_EQ (l2s_nc (l3), "1,17,42,2"); + EXPECT_EQ (l2srm_nc (l3), "1,17,42,2"); + EXPECT_EQ (l2sm_nc (l3), "2,42,17,1"); + EXPECT_EQ (l2sr_nc (l3), "2,42,17,1"); + EXPECT_EQ (l3.size (), size_t (4)); - MyClass2 *c1; - c1 = l3.first (); - EXPECT_EQ (c1->n, 1); - c1 = c1->next (); - EXPECT_EQ (c1->n, 17); - c1 = c1->next (); - EXPECT_EQ (c1->n, 42); - EXPECT_EQ (c1->next (), 0); + l3.pop_back (); + EXPECT_EQ (l2s (l3), "1,17,42"); + EXPECT_EQ (l2sr (l3), "42,17,1"); + EXPECT_EQ (l3.size (), size_t (3)); - c1 = l3.last (); - EXPECT_EQ (c1->n, 42); - c1 = c1->prev (); - EXPECT_EQ (c1->n, 17); - c1 = c1->prev (); - EXPECT_EQ (c1->n, 1); - EXPECT_EQ (c1->prev (), 0); + MyClass2 *c1; + c1 = l3.first (); + EXPECT_EQ (c1->n, 1); + c1 = c1->next (); + EXPECT_EQ (c1->n, 17); + c1 = c1->next (); + EXPECT_EQ (c1->n, 42); + EXPECT_EQ (c1->next (), 0); - l3.pop_front (); - EXPECT_EQ (l2s (l3), "17,42"); - EXPECT_EQ (l2sr (l3), "42,17"); - EXPECT_EQ (l3.size (), size_t (2)); + c1 = l3.last (); + EXPECT_EQ (c1->n, 42); + c1 = c1->prev (); + EXPECT_EQ (c1->n, 17); + c1 = c1->prev (); + EXPECT_EQ (c1->n, 1); + EXPECT_EQ (c1->prev (), 0); - l3.push_back (new MyClass2 (1)); - EXPECT_EQ (l2s (l3), "17,42,1"); - EXPECT_EQ (l2sr (l3), "1,42,17"); - EXPECT_EQ (l3.size (), size_t (3)); + l3.pop_front (); + EXPECT_EQ (l2s (l3), "17,42"); + EXPECT_EQ (l2sr (l3), "42,17"); + EXPECT_EQ (l3.size (), size_t (2)); - c1 = l3.first ()->next (); - delete c1; - EXPECT_EQ (l2s (l3), "17,1"); - EXPECT_EQ (l2sr (l3), "1,17"); - EXPECT_EQ (l3.size (), size_t (2)); + l3.push_back (new MyClass2 (1)); + EXPECT_EQ (l2s (l3), "17,42,1"); + EXPECT_EQ (l2sr (l3), "1,42,17"); + EXPECT_EQ (l3.size (), size_t (3)); + + c1 = l3.first ()->next (); + delete c1; + EXPECT_EQ (l2s (l3), "17,1"); + EXPECT_EQ (l2sr (l3), "1,17"); + EXPECT_EQ (l3.size (), size_t (2)); + + l1.clear (); + l2.clear (); + l3.clear (); + EXPECT_EQ (obj_count, size_t (1)); // one for mc2 + } + + EXPECT_EQ (obj_count, size_t (0)); // mc2 gone as well } From 060abc056e98eb98d6a4b0af7d6f0ce32e228fdf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 31 Dec 2018 17:33:14 +0100 Subject: [PATCH 162/335] Fixed a memory corruption issue from the Netlist destructor. --- src/db/db/dbNetlist.cc | 20 +++++++++++++++++++- src/db/db/dbNetlist.h | 10 ++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 909cf7953..505604177 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -555,11 +555,23 @@ Circuit::Circuit (const Circuit &other) m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), m_index (0) { + operator= (other); m_devices.changed ().add (this, &Circuit::devices_changed); m_nets.changed ().add (this, &Circuit::nets_changed); m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed); +} - operator= (other); +Circuit::~Circuit () +{ + m_devices.changed ().remove (this, &Circuit::devices_changed); + m_nets.changed ().remove (this, &Circuit::nets_changed); + m_subcircuits.changed ().remove (this, &Circuit::subcircuits_changed); + + // the default destructor will make the nets access "this" to unregister the + // objects - hence we have to do this explicitly. + m_nets.clear (); + m_subcircuits.clear (); + m_devices.clear (); } Circuit &Circuit::operator= (const Circuit &other) @@ -1201,6 +1213,12 @@ Netlist::Netlist (const Netlist &other) m_circuits.changed ().add (this, &Netlist::circuits_changed); } +Netlist::~Netlist () +{ + m_circuits.changed ().remove (this, &Netlist::invalidate_topology); + m_circuits.changed ().remove (this, &Netlist::circuits_changed); +} + Netlist &Netlist::operator= (const Netlist &other) { if (this != &other) { diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 9c4909200..73ea82d42 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -1043,6 +1043,11 @@ public: */ Circuit (const Circuit &other); + /** + * @brief Destructor + */ + ~Circuit (); + /** * @brief Assignment */ @@ -2005,6 +2010,11 @@ public: */ Netlist (const Netlist &other); + /** + * @brief Destructor + */ + ~Netlist (); + /** * @brief Assignment */ From fed7a4bfed878c5060df799dee277b6d110d80f4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 1 Jan 2019 13:07:40 +0100 Subject: [PATCH 163/335] Some performance improvement for the net extractor. --- src/db/db/dbHierNetworkProcessor.cc | 30 +++++++++++++++++++++++------ src/db/db/dbHierNetworkProcessor.h | 8 ++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 7f6cc1289..f78fbac7f 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -157,7 +157,7 @@ template DB_PUBLIC bool Connectivity::interacts (const db::Polyg template local_cluster::local_cluster () - : m_id (0), m_needs_update (false) + : m_id (0), m_needs_update (false), m_size (0) { // .. nothing yet .. } @@ -168,6 +168,7 @@ local_cluster::clear () { m_shapes.clear (); m_needs_update = false; + m_size = 0; m_bbox = box_type (); m_attrs.clear (); } @@ -187,6 +188,7 @@ local_cluster::add (const T &s, unsigned int la) { m_shapes[la].insert (s); m_needs_update = true; + ++m_size; } template @@ -194,11 +196,12 @@ void local_cluster::join_with (const local_cluster &other) { for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { - tree_type &other_tree = m_shapes[s->first]; - other_tree.insert (s->second.begin (), s->second.end ()); + tree_type &tree = m_shapes[s->first]; + tree.insert (s->second.begin (), s->second.end ()); } m_attrs.insert (other.m_attrs.begin (), other.m_attrs.end ()); + m_size += other.size (); m_needs_update = true; } @@ -310,6 +313,8 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans return false; } + box_type common_for_other = common.transformed (trans.inverted ()); + db::box_scanner2 scanner; transformed_box bc_t (trans); db::box_convert bc; @@ -326,12 +331,19 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans return false; } + any = false; + for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { - for (typename tree_type::touching_iterator i = s->second.begin_touching (common.transformed (trans.inverted ()), bc); ! i.at_end (); ++i) { + for (typename tree_type::touching_iterator i = s->second.begin_touching (common_for_other, bc); ! i.at_end (); ++i) { scanner.insert2 (i.operator-> (), s->first); + any = true; } } + if (! any) { + return false; + } + interaction_receiver rec (conn, trans); return ! scanner.process (rec, 1 /*==touching*/, bc, bc_t); } @@ -906,6 +918,12 @@ private: } else if (x1 != x2) { + // for instance-to-instance interactions the number of connections is more important for the + // cost of the join operation: make the one with more connections the target + if (mp_cell_clusters->connections_for_cluster (x1).size () < mp_cell_clusters->connections_for_cluster (x2).size ()) { + std::swap (x1, x2); + } + mp_cell_clusters->join_cluster_with (x1, x2); mp_cell_clusters->remove_cluster (x2); @@ -1258,7 +1276,7 @@ hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell if (tl::verbosity () >= 40) { tl::log << msg; } - tl::SelfTimer (tl::verbosity () >= 41, msg); + tl::SelfTimer timer (tl::verbosity () >= 41, msg); connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; local.build_clusters (cell, shape_flags, conn); @@ -1281,7 +1299,7 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c if (tl::verbosity () >= 40) { tl::log << msg; } - tl::SelfTimer (tl::verbosity () >= 41, msg); + tl::SelfTimer timer (tl::verbosity () >= 41, msg); connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index d91b4d287..21b3de313 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -193,6 +193,13 @@ public: return m_bbox; } + /** + * @brief Gets the total number of shapes in this cluster + */ size_t size () const + { + return m_size; + } + /** * @brief Gets the shape iterator for a given layer */ @@ -246,6 +253,7 @@ private: std::map m_shapes; box_type m_bbox; attr_set m_attrs; + size_t m_size; }; /** From 7898ec37cd84b3b64d01a3376ba95ae83183b812 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 1 Jan 2019 17:23:11 +0100 Subject: [PATCH 164/335] Bugfix: netlist extractor did loose some connections. --- src/db/db/dbHierNetworkProcessor.cc | 95 ++++++++++++++++++----------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index f78fbac7f..9635b9bde 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -467,59 +467,78 @@ struct cluster_building_receiver : public db::box_scanner_receiver > { typedef typename local_cluster::id_type id_type; + typedef std::pair > shape_value; - cluster_building_receiver (local_clusters &clusters, const db::Connectivity &conn) - : mp_clusters (&clusters), mp_conn (&conn) + cluster_building_receiver (const db::Connectivity &conn) + : mp_conn (&conn) { // .. nothing yet.. } + void generate_clusters (local_clusters &clusters) + { + // build the resulting clusters + for (typename std::list >::const_iterator c = m_clusters.begin (); c != m_clusters.end (); ++c) { + + // TODO: reserve? + local_cluster *cluster = clusters.insert (); + for (typename std::vector::const_iterator s = c->begin (); s != c->end (); ++s) { + cluster->add (*s->first, s->second.first); + cluster->add_attr (s->second.second); + } + + } + } + void add (const T *s1, std::pair p1, const T *s2, std::pair p2) { if (! mp_conn->interacts (*s1, p1.first, *s2, p2.first)) { return; } - typename std::map::const_iterator id1 = m_shape_to_cluster_id.find (s1); - typename std::map::const_iterator id2 = m_shape_to_cluster_id.find (s2); + typename std::map >::iterator>::iterator ic1 = m_shape_to_clusters.find (s1); + typename std::map >::iterator>::iterator ic2 = m_shape_to_clusters.find (s2); - if (id1 == m_shape_to_cluster_id.end ()) { + if (ic1 == m_shape_to_clusters.end ()) { - if (id2 == m_shape_to_cluster_id.end ()) { + if (ic2 == m_shape_to_clusters.end ()) { - local_cluster *cluster = mp_clusters->insert (); - cluster->add (*s1, p1.first); - cluster->add (*s2, p2.first); - cluster->add_attr (p1.second); - cluster->add_attr (p2.second); + m_clusters.push_back (std::vector ()); + typename std::list >::iterator c = --m_clusters.end (); + c->push_back (std::make_pair (s1, p1)); + c->push_back (std::make_pair (s2, p2)); - m_shape_to_cluster_id.insert (std::make_pair (s1, cluster->id ())); - m_shape_to_cluster_id.insert (std::make_pair (s2, cluster->id ())); + m_shape_to_clusters.insert (std::make_pair (s1, c)); + m_shape_to_clusters.insert (std::make_pair (s2, c)); } else { - // NOTE: const_cast is in order - we know what we're doing. - local_cluster &c2 = const_cast &> (mp_clusters->cluster_by_id (id2->second)); - c2.add (*s1, p1.first); - c2.add_attr (p1.second); - m_shape_to_cluster_id.insert (std::make_pair (s1, id2->second)); + ic2->second->push_back (std::make_pair (s1, p1)); + m_shape_to_clusters.insert (std::make_pair (s1, ic2->second)); } - } else if (id2 == m_shape_to_cluster_id.end ()) { + } else if (ic2 == m_shape_to_clusters.end ()) { - // NOTE: const_cast is in order - we know what we're doing. - local_cluster &c1 = const_cast &> (mp_clusters->cluster_by_id (id1->second)); - c1.add (*s2, p2.first); - c1.add_attr (p2.second); - m_shape_to_cluster_id.insert (std::make_pair (s2, id1->second)); + ic1->second->push_back (std::make_pair (s2, p2)); + m_shape_to_clusters.insert (std::make_pair (s2, ic1->second)); - } else if (id1->second != id2->second) { + } else if (ic1->second != ic2->second) { - // this shape connects two clusters: join them - // NOTE: const_cast is in order - we know what we're doing. - const_cast &> (mp_clusters->cluster_by_id (id1->second)).join_with (mp_clusters->cluster_by_id (id2->second)); - mp_clusters->remove_cluster (id2->second); + // join clusters: use the larger one as the target + + if (ic1->second->size () < ic2->second->size ()) { + std::swap (ic1, ic2); + } + + ic1->second->insert (ic1->second->end (), ic2->second->begin (), ic2->second->end ()); + + typename std::list >::iterator j = ic2->second; + for (typename std::vector::const_iterator i = j->begin (); i != j->end (); ++i) { + m_shape_to_clusters [i->first] = ic1->second; + } + + m_clusters.erase (j); } } @@ -527,18 +546,19 @@ struct cluster_building_receiver void finish (const T *s, std::pair p) { // if the shape has not been handled yet, insert a single cluster with only this shape - typename std::map::const_iterator id = m_shape_to_cluster_id.find (s); - if (id == m_shape_to_cluster_id.end ()) { - local_cluster *cluster = mp_clusters->insert (); - cluster->add (*s, p.first); - cluster->add_attr (p.second); + typename std::map >::iterator>::const_iterator ic = m_shape_to_clusters.find (s); + if (ic == m_shape_to_clusters.end ()) { + m_clusters.push_back (std::vector ()); + typename std::list >::iterator c = --m_clusters.end (); + c->push_back (std::make_pair (s, p)); + m_shape_to_clusters.insert (std::make_pair (s, c)); } } private: - local_clusters *mp_clusters; const db::Connectivity *mp_conn; - std::map m_shape_to_cluster_id; + std::map >::iterator> m_shape_to_clusters; + std::list > m_clusters; }; } @@ -561,8 +581,9 @@ local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flag } } - cluster_building_receiver rec (*this, conn); + cluster_building_receiver rec (conn); bs.process (rec, 1 /*==touching*/, bc); + rec.generate_clusters (*this); } // explicit instantiations From 6a710a9efb3740a69d1325ea88a626ccd6d25685 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 1 Jan 2019 19:19:53 +0100 Subject: [PATCH 165/335] Fixed an internal error that happened if additional layers got added to the working shape set. --- src/db/db/dbBoxScanner.h | 3 +- src/db/db/dbHierarchyBuilder.cc | 59 ++++++++++++++++++--------------- src/db/db/dbHierarchyBuilder.h | 3 +- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index e8c46c693..5322a0686 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -278,8 +278,9 @@ public: // below m_scanner_thr elements use the brute force approach which is faster in that case for (iterator_type i = m_pp.begin (); i != m_pp.end (); ++i) { + box_type b1 = bc (*i->first); for (iterator_type j = i + 1; j != m_pp.end (); ++j) { - if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) { + if (bs_boxes_overlap (b1, bc (*j->first), enl)) { rec.add (i->first, i->second, j->first, j->second); if (rec.stop ()) { return false; diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 4a64de4d8..0d9465d04 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -126,13 +126,13 @@ static std::pair > compute_clip_variant (const db::Box & } HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, HierarchyBuilderShapeReceiver *pipe) - : mp_target (target), m_initial_pass (true), m_target_layer (target_layer) + : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer) { set_shape_receiver (pipe); } HierarchyBuilder::HierarchyBuilder (db::Layout *target, HierarchyBuilderShapeReceiver *pipe) - : mp_target (target), m_initial_pass (true), m_target_layer (0) + : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0) { set_shape_receiver (pipe); } @@ -158,6 +158,7 @@ HierarchyBuilder::reset () m_cells_seen.clear (); m_cell_stack.clear (); m_cm_entry = cell_map_type::const_iterator (); + m_cm_new_entry = false; } void @@ -174,17 +175,18 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter) std::pair > key (iter->top_cell ()->cell_index (), std::set ()); m_cm_entry = m_cell_map.find (key); - if (m_cm_entry == m_cell_map.end ()) { + m_cm_new_entry = false; + if (m_cm_entry == m_cell_map.end ()) { db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.first)); m_cm_entry = m_cell_map.insert (std::make_pair (key, new_top_index)).first; - + m_cm_new_entry = true; } db::Cell &new_top = mp_target->cell (m_cm_entry->second); m_cells_seen.insert (key); - m_cell_stack.push_back (&new_top); + m_cell_stack.push_back (std::make_pair (m_cm_new_entry, &new_top)); } void @@ -194,9 +196,10 @@ HierarchyBuilder::end (const RecursiveShapeIterator * /*iter*/) m_initial_pass = false; m_cells_seen.clear (); - mp_initial_cell = m_cell_stack.front (); + mp_initial_cell = m_cell_stack.front ().second; m_cell_stack.clear (); m_cm_entry = cell_map_type::const_iterator (); + m_cm_new_entry = false; } void @@ -205,7 +208,7 @@ HierarchyBuilder::enter_cell (const RecursiveShapeIterator * /*iter*/, const db: tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ()); m_cells_seen.insert (m_cm_entry->first); - m_cell_stack.push_back (&mp_target->cell (m_cm_entry->second)); + m_cell_stack.push_back (std::make_pair (m_cm_new_entry, &mp_target->cell (m_cm_entry->second))); } void @@ -221,18 +224,19 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn std::pair > key (inst.object ().cell_index (), std::set ()); m_cm_entry = m_cell_map.find (key); + m_cm_new_entry = false; - if (m_initial_pass) { - - if (m_cm_entry == m_cell_map.end ()) { - db::cell_index_type new_cell = mp_target->add_cell (iter->layout ()->cell_name (inst.object ().cell_index ())); - m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; - } + if (m_cm_entry == m_cell_map.end ()) { + db::cell_index_type new_cell = mp_target->add_cell (iter->layout ()->cell_name (inst.object ().cell_index ())); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + m_cm_new_entry = true; + } + // for new cells, create this instance + if (m_cell_stack.back ().first) { db::CellInstArray new_inst = inst; new_inst.object () = db::CellInst (m_cm_entry->second); - m_cell_stack.back ()->insert (new_inst); - + m_cell_stack.back ().second->insert (new_inst); } // To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array. @@ -263,21 +267,22 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: std::pair > key (inst.object ().cell_index (), clip_variant.second); m_cm_entry = m_cell_map.find (key); + m_cm_new_entry = false; - if (m_initial_pass) { - - if (m_cm_entry == m_cell_map.end ()) { - std::string suffix; - if (! key.second.empty ()) { - suffix = "$CLIP_VAR"; - } - db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ()); - m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + if (m_cm_entry == m_cell_map.end ()) { + std::string suffix; + if (! key.second.empty ()) { + suffix = "$CLIP_VAR"; } + db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ()); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + m_cm_new_entry = true; + } + // for a new cell, create this instance + if (m_cell_stack.back ().first) { db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans); - m_cell_stack.back ()->insert (new_inst); - + m_cell_stack.back ().second->insert (new_inst); } return (m_cells_seen.find (key) == m_cells_seen.end ()); @@ -288,7 +293,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: void HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/, const db::Box ®ion, const box_tree_type *complex_region) { - db::Shapes &shapes = m_cell_stack.back ()->shapes (m_target_layer); + db::Shapes &shapes = m_cell_stack.back ().second->shapes (m_target_layer); mp_pipe->push (shape, region, complex_region, &shapes); } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 6b7cd6448..7a2c23f84 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -249,8 +249,9 @@ private: cell_map_type m_cell_map; std::set m_cells_seen; cell_map_type::const_iterator m_cm_entry; + bool m_cm_new_entry; unsigned int m_target_layer; - std::vector m_cell_stack; + std::vector > m_cell_stack; db::Cell *mp_initial_cell; }; From ec3198c4662c2f2a478df91375d43d1b84720028 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 1 Jan 2019 22:39:22 +0100 Subject: [PATCH 166/335] Netlist extraction performance improvement by taking a shortcut for local cluster/child cell cluster interactions. --- src/db/db/dbHierNetworkProcessor.cc | 48 +++++++++++++++++++++++------ src/db/db/dbHierNetworkProcessor.h | 8 +++++ 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 9635b9bde..80407fdfd 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -302,6 +302,30 @@ typename local_cluster::shape_iterator local_cluster::begin (unsigned int } } +template +bool +local_cluster::interacts (const db::Cell &cell, const db::ICplxTrans &trans, const Connectivity &conn) const +{ + db::box_convert bc; + + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + + db::Box box; + + Connectivity::layer_iterator le = conn.end_connected (s->first); + for (Connectivity::layer_iterator l = conn.begin_connected (s->first); l != le; ++l) { + box += cell.bbox (*l); + } + + if (! box.empty () && ! s->second.begin_touching (box.transformed (trans), bc).at_end ()) { + return true; + } + + } + + return false; +} + template bool local_cluster::interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const @@ -970,14 +994,23 @@ private: box_type b1 = c1.bbox (); box_type bb2 = (*mp_cbc) (i2.cell_index ()); - box_type b2 = i2.cell_inst ().bbox (*mp_cbc).transformed (t2); - if (! b1.touches (b2)) { + db::ICplxTrans t2i = t2 * i2.complex_trans (); + const db::Cell &cell2 = mp_layout->cell (i2.cell_index ()); + + box_type b2 = cell2.bbox ().transformed (t2i); + + if (! b1.touches (b2) || ! c1.interacts (cell2, t2i, *mp_conn)) { return; } box_type common = b1 & b2; + std::vector pp2; + pp2.reserve (p2.size () + 1); + pp2.insert (pp2.end (), p2.begin (), p2.end ()); + pp2.push_back (db::InstElement ()); + for (db::CellInstArray::iterator ii2 = i2.begin_touching (common.transformed (t2.inverted ()), mp_layout); ! ii2.at_end (); ++ii2) { db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); @@ -985,15 +1018,10 @@ private: if (b1.touches (ib2)) { - std::vector pp2; - pp2.reserve (p2.size () + 1); - pp2.insert (pp2.end (), p2.begin (), p2.end ()); - pp2.push_back (db::InstElement (i2, ii2)); - + pp2.back () = db::InstElement (i2, ii2); add_single_pair (c1, i2.cell_index (), pp2, tt2); // dive into cell of ii2 - const db::Cell &cell2 = mp_layout->cell (i2.cell_index ()); for (db::Cell::touching_iterator jj2 = cell2.begin_touching (common.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { add_pair (c1, *jj2, pp2, tt2); } @@ -1402,7 +1430,9 @@ connected_clusters & hier_clusters::clusters_per_cell (db::cell_index_type cell_index) { typename std::map >::iterator c = m_per_cell_clusters.find (cell_index); - tl_assert (c != m_per_cell_clusters.end ()); + if (c == m_per_cell_clusters.end ()) { + c = m_per_cell_clusters.insert (std::make_pair (cell_index, connected_clusters ())).first; + } return c->second; } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 21b3de313..d65d4c5f7 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -184,6 +184,14 @@ public: */ bool interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const; + /** + * @brief Tests whether this cluster interacts with the given cell + * + * "trans" is the transformation which is applied to the cell before + * the test. + */ + bool interacts (const db::Cell &cell, const db::ICplxTrans &trans, const Connectivity &conn) const; + /** * @brief Gets the bounding box of this cluster */ From 76330bea3a85d1fd7d55efca19b4b50115af40f8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 2 Jan 2019 23:23:04 +0100 Subject: [PATCH 167/335] Save some memory on net shape retrieval. --- src/db/db/dbLayoutToNetlist.cc | 10 +++++----- src/db/db/dbLayoutToNetlist.h | 6 +++++- src/db/db/gsiDeclDbLayoutToNetlist.cc | 2 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 12 ++++++------ 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 4ac11e41c..b8491b9ac 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -209,10 +209,10 @@ const db::hier_clusters &LayoutToNetlist::net_clusters () const return m_netex.clusters (); } -db::Region LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const +db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const { unsigned int lid = layer_of (of_layer); - db::Region res; + std::auto_ptr res (new db::Region ()); if (! recursive) { @@ -224,7 +224,7 @@ db::Region LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region const db::local_cluster &lc = m_netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); for (db::local_cluster::shape_iterator s = lc.begin (lid); !s.at_end (); ++s) { - res.insert (s->obj ().transformed (s->trans ())); + res->insert (s->obj ().transformed (s->trans ())); } } else { @@ -235,12 +235,12 @@ db::Region LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region db::cell_index_type ci = circuit->cell_index (); for (db::recursive_cluster_shape_iterator rci (m_netex.clusters (), lid, ci, net.cluster_id ()); !rci.at_end (); ++rci) { - res.insert (rci->obj ().transformed (rci.trans () * db::ICplxTrans (rci->trans ()))); + res->insert (rci->obj ().transformed (rci.trans () * db::ICplxTrans (rci->trans ()))); } } - return res; + return res.release (); } db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point) diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 8a9a8efbd..09834dcf1 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -215,10 +215,14 @@ public: /** * @brief Returns all shapes of a specific net and layer. + * * If "recursive" is true, the returned region will contain the shapes of * all subcircuits too. + * + * This methods returns a new'd Region. It's the responsibility of the caller + * to delete this object. */ - db::Region shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const; + db::Region *shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const; /** * @brief Finds the net by probing a specific location on the given layer diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 900c906f5..6cee2d264 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -141,7 +141,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("netlist", &db::LayoutToNetlist::netlist, "@brief gets the netlist extracted (0 if no extraction happened yet)\n" ) + - gsi::method ("shapes_of_net", &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), + gsi::factory ("shapes_of_net", &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), "@brief Returns all shapes of a specific net and layer.\n" "If 'recursive'' is true, the returned region will contain the shapes of\n" "all subcircuits too.\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 2254c9b23..e70ab822c 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -114,8 +114,8 @@ static void dump_nets_to_layout (const db::LayoutToNetlist &l2n, db::Layout &ly, for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { - db::Region shapes = l2n.shapes_of_net (*n, *m->first, false); - if (shapes.empty ()) { + std::auto_ptr shapes (l2n.shapes_of_net (*n, *m->first, false)); + if (shapes->empty ()) { continue; } @@ -125,7 +125,7 @@ static void dump_nets_to_layout (const db::LayoutToNetlist &l2n, db::Layout &ly, cell.insert (db::CellInstArray (db::CellInst (nci), db::Trans ())); } - shapes.insert_into (&ly, nci, m->second); + shapes->insert_into (&ly, nci, m->second); } @@ -152,8 +152,8 @@ static void dump_recursive_nets_to_layout (const db::LayoutToNetlist &l2n, db::L for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { - db::Region shapes = l2n.shapes_of_net (*n, *m->first, true); - if (shapes.empty ()) { + std::auto_ptr shapes (l2n.shapes_of_net (*n, *m->first, true)); + if (shapes->empty ()) { continue; } @@ -163,7 +163,7 @@ static void dump_recursive_nets_to_layout (const db::LayoutToNetlist &l2n, db::L cell.insert (db::CellInstArray (db::CellInst (nci), db::Trans ())); } - shapes.insert_into (&ly, nci, m->second); + shapes->insert_into (&ly, nci, m->second); } From 20799026d1f4f9e951cdc6199f51d0c4984d0d2a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 3 Jan 2019 18:26:18 +0100 Subject: [PATCH 168/335] WIP: bugfix in net extraction (wrong hierarchy treatment) --- src/db/db/dbHierNetworkProcessor.cc | 126 ++++++++++++++++-- src/db/db/dbHierNetworkProcessor.h | 24 +++- .../unit_tests/dbHierNetworkProcessorTests.cc | 32 +++++ 3 files changed, 172 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 80407fdfd..77f33297e 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -156,8 +156,8 @@ template DB_PUBLIC bool Connectivity::interacts (const db::Polyg // local_cluster implementation template -local_cluster::local_cluster () - : m_id (0), m_needs_update (false), m_size (0) +local_cluster::local_cluster (size_t id) + : m_id (id), m_needs_update (false), m_size (0) { // .. nothing yet .. } @@ -372,9 +372,104 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans return ! scanner.process (rec, 1 /*==touching*/, bc, bc_t); } +template +double local_cluster::area_ratio () const +{ + box_type bx = bbox (); + if (bx.empty ()) { + return 0.0; + } + + db::box_convert bc; + + // just the sum of the areas of the bounding boxes - this is precise if no overlaps happen and the + // polygons are rather rectangular. This criterion is coarse enough to prevent recursion in the split + // algorithm and still be fine enough - consider that we a planning to use splitted polygons for + // which the bbox is a fairly good approximation. + typename box_type::area_type a = 0; + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + for (typename tree_type::const_iterator i = s->second.begin (); i != s->second.end (); ++i) { + a += bc (*i).area (); + } + } + + return (a == 0 ? 0.0 : double (bx.area ()) / double (a)); +} + +template +std::vector +local_cluster::layers () const +{ + std::vector l; + l.reserve (m_shapes.size ()); + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + l.push_back (s->first); + } + return l; +} + +template +size_t split_cluster (const local_cluster &cl, double max_area_ratio, Iter &output) +{ + if (cl.area_ratio () < max_area_ratio) { + return 0; // no splitting happened + } + + db::box_convert bc; + typename local_cluster::box_type bx = cl.bbox (); + + int xthr = bx.width () > bx.height () ? bx.center ().x () : bx.left (); + int ythr = bx.width () > bx.height () ? bx.bottom () : bx.center ().y (); + + // split along the longer axis - decide the position according to the bbox center + + local_cluster a (cl.id ()), b (cl.id ()); + + std::vector layers = cl.layers (); + + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + for (typename local_cluster::shape_iterator s = cl.begin (*l); ! s.at_end (); ++s) { + typename local_cluster::box_type::point_type sc = bc (*s).center (); + if (sc.x () < xthr || sc.y () < ythr) { + a.add (*s, *l); + } else { + b.add (*s, *l); + } + } + } + + if (a.size () == 0 || b.size () == 0) { + // give up to prevent infinite recursion + return 0; + } + + // split further if required + size_t na = split_cluster (a, max_area_ratio, output); + size_t nb = split_cluster (b, max_area_ratio, output); + + if (na == 0) { + *output++ = a; + na = 1; + } + + if (nb == 0) { + *output++ = b; + nb = 1; + } + + return na + nb; +} + +template +template +size_t local_cluster::split (double max_area_ratio, Iter &output) const +{ + return split_cluster (*this, max_area_ratio, output); +} + // explicit instantiations template class DB_PUBLIC local_cluster; - +template DB_PUBLIC size_t local_cluster::split > > > (double, std::back_insert_iterator > > &) const; // ------------------------------------------------------------------------------ // local_clusters implementation @@ -862,23 +957,22 @@ private: return; } + db::ICplxTrans t1i = t1.inverted (); db::ICplxTrans t2i = t2.inverted (); - db::ICplxTrans t12 = t2i * t1; box_type common = b1 & b2; - for (db::CellInstArray::iterator ii1 = i1.begin_touching (common, mp_layout); ! ii1.at_end (); ++ii1) { + for (db::CellInstArray::iterator ii1 = i1.begin_touching (common.transformed (t1i), mp_layout); ! ii1.at_end (); ++ii1) { db::ICplxTrans tt1 = t1 * i1.complex_trans (*ii1); box_type ib1 = bb1.transformed (tt1); - box_type ib12 = ib1.transformed (t12); std::vector pp1; pp1.reserve (p1.size () + 1); pp1.insert (pp1.end (), p1.begin (), p1.end ()); pp1.push_back (db::InstElement (i1, ii1)); - for (db::CellInstArray::iterator ii2 = i2.begin_touching (ib12, mp_layout); ! ii2.at_end (); ++ii2) { + for (db::CellInstArray::iterator ii2 = i2.begin_touching (ib1.transformed (t2i), mp_layout); ! ii2.at_end (); ++ii2) { db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); box_type ib2 = bb2.transformed (tt2); @@ -1392,6 +1486,9 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // handle local to instance connections { + std::list > heap; + double area_ratio = 10.0; + static std::string desc = tl::to_string (tr ("Local to instance treatment")); tl::SelfTimer timer (tl::verbosity () >= 51, desc); @@ -1399,8 +1496,21 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c db::box_scanner2, unsigned int, db::Instance, unsigned int> bs2 (report_progress, desc); for (typename connected_clusters::const_iterator c = local.begin (); c != local.end (); ++c) { - bs2.insert1 (c.operator-> (), 0); + + // we do not actually need the original clusters. For a better performance we optimize the + // area ratio and split, but we keep the ID the same. + std::back_insert_iterator > > iout = std::back_inserter (heap); + size_t n = c->split (area_ratio, iout); + if (n == 0) { + bs2.insert1 (c.operator-> (), 0); + } else { + std::list >::iterator h = heap.end (); + while (n-- > 0) { + bs2.insert1 ((--h).operator-> (), 0); + } + } } + for (std::vector::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) { bs2.insert2 (inst.operator-> (), 0); } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index d65d4c5f7..e1e670b82 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -146,7 +146,7 @@ public: /** * @brief Creates an empty cluster */ - local_cluster (); + local_cluster (size_t id = 0); /** * @brief Clears the cluster @@ -201,9 +201,29 @@ public: return m_bbox; } + /** + * @brief Computes the "area ratio" of the cluster - this is a rough approximation of the area covered + * The algorithm used assumes no overlap between the polygons of the cluster. + */ + double area_ratio () const; + + /** + * @brief Splits the cluster into multiple other clusters until the desired area ratio is achieved. + * The result is sent to the output iterator. The return value is the number of clusters produced. + * If the area ratio of the cluster is below the limit, no splitting happens and 0 is returned. + */ + template + size_t split (double max_area_ratio, Iter &output) const; + + /** + * @brief Gets a vector of layers inside the cluster + */ + std::vector layers () const; + /** * @brief Gets the total number of shapes in this cluster - */ size_t size () const + */ + size_t size () const { return m_size; } diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 0fba6ec8e..b58f0460a 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -275,6 +275,38 @@ static std::string local_clusters_to_string (const db::local_clusters &cluste return s; } +TEST(12_LocalClusterSplitByAreaRatio) +{ + db::GenericRepository repo; + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + + db::local_cluster cluster (17); + cluster.add (db::PolygonRef (db::Polygon (db::Box (0, 0, 20, 20)), repo), 0); + cluster.add (db::PolygonRef (db::Polygon (db::Box (0, 0, 1000, 20)), repo), 0); + cluster.add (db::PolygonRef (db::Polygon (db::Box (1000, 0, 1020, 1000)), repo), 1); + cluster.add (db::PolygonRef (db::Polygon (db::Box (0, 1000, 1000, 1020)), repo), 2); + + std::list > out; + std::back_insert_iterator > > iout = std::back_inserter (out); + size_t n = cluster.split (10.0, iout); + + EXPECT_EQ (n, size_t (3)); + EXPECT_EQ (out.size (), size_t (3)); + + std::list >::const_iterator i = out.begin (); + EXPECT_EQ (local_cluster_to_string (*i, conn), "[0](0,0;0,20;20,20;20,0);[0](0,0;0,20;1000,20;1000,0)"); + EXPECT_EQ (i->id (), size_t (17)); + ++i; + EXPECT_EQ (local_cluster_to_string (*i, conn), "[1](1000,0;1000,1000;1020,1000;1020,0)"); + EXPECT_EQ (i->id (), size_t (17)); + ++i; + EXPECT_EQ (local_cluster_to_string (*i, conn), "[2](0,1000;0,1020;1000,1020;1000,1000)"); + EXPECT_EQ (i->id (), size_t (17)); +} + TEST(20_LocalClustersBasic) { db::Layout layout; From 62d9941c4a65cea01bc31eaf1414dcc2b060aa0d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 3 Jan 2019 22:09:19 +0100 Subject: [PATCH 169/335] WIP: Bugfix - hierarchy was dropping instances. --- src/db/db/dbHierarchyBuilder.cc | 11 ++++++- src/db/db/dbHierarchyBuilder.h | 1 + src/db/unit_tests/dbHierarchyBuilderTests.cc | 31 +++++++++++++++++++ testdata/algo/hierarchy_builder_au_l4.gds | Bin 0 -> 3566 bytes testdata/algo/hierarchy_builder_l4.gds | Bin 0 -> 3566 bytes 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 testdata/algo/hierarchy_builder_au_l4.gds create mode 100644 testdata/algo/hierarchy_builder_l4.gds diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 0d9465d04..88039a4b4 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -154,6 +154,7 @@ HierarchyBuilder::reset () m_initial_pass = true; mp_initial_cell = 0; + m_cells_to_be_filled.clear (); m_cell_map.clear (); m_cells_seen.clear (); m_cell_stack.clear (); @@ -206,9 +207,15 @@ void HierarchyBuilder::enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ()); + m_cells_seen.insert (m_cm_entry->first); - m_cell_stack.push_back (std::make_pair (m_cm_new_entry, &mp_target->cell (m_cm_entry->second))); + bool new_cell = (m_cells_to_be_filled.find (m_cm_entry->second) != m_cells_to_be_filled.end ()); + if (new_cell) { + m_cells_to_be_filled.erase (m_cm_entry->second); + } + + m_cell_stack.push_back (std::make_pair (new_cell, &mp_target->cell (m_cm_entry->second))); } void @@ -230,6 +237,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn db::cell_index_type new_cell = mp_target->add_cell (iter->layout ()->cell_name (inst.object ().cell_index ())); m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; m_cm_new_entry = true; + m_cells_to_be_filled.insert (new_cell); } // for new cells, create this instance @@ -277,6 +285,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ()); m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; m_cm_new_entry = true; + m_cells_to_be_filled.insert (new_cell); } // for a new cell, create this instance diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 7a2c23f84..5e0da406f 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -248,6 +248,7 @@ private: db::RecursiveShapeIterator m_source; cell_map_type m_cell_map; std::set m_cells_seen; + std::set m_cells_to_be_filled; cell_map_type::const_iterator m_cm_entry; bool m_cm_new_entry; unsigned int m_target_layer; diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc index 55e0d6e76..317b5883f 100644 --- a/src/db/unit_tests/dbHierarchyBuilderTests.cc +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -467,3 +467,34 @@ TEST(5_CompareRecursiveShapeIterators) } } +TEST(6_DisjunctLayersPerHierarchyBranch) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l4.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + + iter.push (&builder); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au_l4.gds"); +} + diff --git a/testdata/algo/hierarchy_builder_au_l4.gds b/testdata/algo/hierarchy_builder_au_l4.gds new file mode 100644 index 0000000000000000000000000000000000000000..ecabac16a26d037eda78e9b70ed50d87f6ec4c3a GIT binary patch literal 3566 zcma)8QD_ub6umn;`(}0%qtzf-Lb1}83aOP^l_E;q#@3LMNOlcv2qd5V5el`Xh!sHv zs|W!Lg8Css?T3G2LC~)vrOlV0LQ4tsuOI~Sl|^?vcka78?@h9^S=c?xz4yHP-o59( z88Q?EU(!%1i2kDzm1#Q}de8k&abUg}s}l{DD@Tt0ba4ESUza}_ee~g-TQf}>s&wbo zjA>3CooEn+F_GC6P(WrNAfn+d0=9EvjAf zSEYVsdw%maS25mP#&3%Tw=hzYyj)@=D({qCN3@~-)I0q#Zhc=p!TBztKToi)vpCy3 zifU){>f`XSjek-T$SE2>#ozuqAOS}YcE90E2xh_tya($*5nICwKICPa`@Qcw$u+~9%{8oQSHTi zu5tNe><}2BS{E(sA zqgeR;9W8GUaO0%)W#z(ecA(D^)k6`uTUZfw9M|c3T~_O&i8}Sx#G06OG~r}XP1yNV z{X6qn`kHUMbS8=Z#CGSwv4|7%tn$fIte*BNtgEF8dpV4*%wooeiK?few4W8#&RD=- zVui>Zp~_HHdrl$fM|NAcE@S;8(&5tY(qTchGkW=ReC&6R@cSPKUs3JVJs!W3<;R5V z!v4<)hSNaYmWpZzx(avrQ1)HO?xtXbCr|4f>TXf(=BCGwviz973pn3n!Q?me@JIZ& zsP^{bB0f0d%*Wo3F+W4K(|9qzdSCdpUxcry_Uip&e%~SC_wN_JqT2fo3ExgA^rsWr z(P_NHOrC6_^!!xrJ)=`&6Q57w`64rFCl(Wr{PIcHS?jHxNZnn$gLjI&Z!H&jcaIc# z_pNzSxO+alRKz{Lx5ztnzQ{ZKEc4nYTWdz!J!{p?NO>T$%4K>ff7 zxn^)bE2`a$cTL3FR1n+{@l01XJp>|sSKHK{i)up`f>hD{d3ERkBQaa_|&R@y*TcoJjXtR z>Ul=GMIjKYO*a}nR>Sj$<3>kq+y-Y3(U_`utGqX@H*uaXH*cOVH*KCTH*20RH))

oiT@QdB!5RbHcRe5~?!<_9v5 zC>mE(d(Mw(O*_pqVXw|k&tj#wvD352@E0;&Q&c;nx6_Kt??mt~;uo0S0sfPt+B=IO zK2AH!eB|B7{2bLzv%UC;-2u*`V0s7G;rxPX@137{oL{iCk9qjDsCLa?lS-HE`OTYL z?_@(6f7>*)k&)8$`4S^h`3>21|N2Z!&3w>6ed~J(II~yE+ny&neHoPGiL%r<_xOa++=r!B5z-X zu8QfRP$vsc>h^?Ap-fXRp5ZsP=*;z5TDu;WAq2X<*E6CvH_y?WX1JzvT8bYdKCtlk5K!O)9D#vnjhj+Y`vI zJt4CMezO&ImZ&}*f%kuUV(RE|y#F$D?F`R44EMl4S+>>Q`lli9(5;Yn`*O&;b1dZD zvk>xrv=s6l+a2;wUkrKY{>Z&}CRTkb6>HyqBDH7iQG0KmuN*_%7sY5@`G)06`#=B9OT`^lLtnVzYU6>8mUXJK s&hHCib@8j+vW`qeJ3;MB>8z90lzA6MR>j+|-qTNreq0bQPF< Date: Thu, 3 Jan 2019 23:25:28 +0100 Subject: [PATCH 170/335] WIP: bugfix - hierarchical net extractor wasn't considering self-interactions between instance array elements. --- src/db/db/dbHierNetworkProcessor.cc | 73 ++++++++++++++++++ .../unit_tests/dbHierNetworkProcessorTests.cc | 14 +++- testdata/algo/hc_test_au11b.gds | Bin 0 -> 14196 bytes testdata/algo/hc_test_au12.gds | Bin 0 -> 16598 bytes testdata/algo/hc_test_au12b.gds | Bin 0 -> 14128 bytes testdata/algo/hc_test_au13.gds | Bin 0 -> 17636 bytes testdata/algo/hc_test_au13b.gds | Bin 0 -> 14996 bytes testdata/algo/hc_test_l12.gds | Bin 0 -> 1008 bytes testdata/algo/hc_test_l13.gds | Bin 0 -> 916 bytes 9 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 testdata/algo/hc_test_au11b.gds create mode 100644 testdata/algo/hc_test_au12.gds create mode 100644 testdata/algo/hc_test_au12b.gds create mode 100644 testdata/algo/hc_test_au13.gds create mode 100644 testdata/algo/hc_test_au13b.gds create mode 100644 testdata/algo/hc_test_l12.gds create mode 100644 testdata/algo/hc_test_l13.gds diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 77f33297e..6a87619f4 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -885,6 +885,16 @@ public: add_pair (*i1, p, t, *i2, p, t); } + /** + * @brief Single-instance treatment - may be required because of interactions between array members + */ + void finish (const db::Instance *i, unsigned int /*p1*/) + { + if (i->size () > 1) { + add_single_inst (*i); + } + } + /** * @brief Receiver main event for local-to-instance interactions */ @@ -1076,6 +1086,69 @@ private: } + /** + * @brief Single instance treatment + */ + void add_single_inst (const db::Instance &i) + { + box_type bb = (*mp_cbc) (i.cell_index ()); + const db::Cell &cell = mp_layout->cell (i.cell_index ()); + + for (db::CellInstArray::iterator ii = i.begin (); ! ii.at_end (); ++ii) { + + db::ICplxTrans tt = i.complex_trans (*ii); + box_type ib = bb.transformed (tt); + + std::vector pp; + pp.push_back (db::InstElement (i, ii)); + + bool any = false; + bool first = true; + + for (db::CellInstArray::iterator ii2 = i.begin_touching (ib, mp_layout); ! ii2.at_end (); ++ii2) { + + db::ICplxTrans tt2 = i.complex_trans (*ii2); + if (tt.equal (tt2)) { + // skip the initial instance + continue; + } + + box_type ib2 = bb.transformed (tt2); + + if (ib.touches (ib2)) { + + std::vector pp2; + pp2.push_back (db::InstElement (i, ii2)); + + box_type common = (ib & ib2); + add_single_pair (common, i.cell_index (), pp, tt, i.cell_index (), pp2, tt2); + + // dive into cell of ii2 - this is a self-interaction of a cell with parts of itself + // as these self-interactions are expected to be the same always (regular array), we can skip this test the next times. + if (first) { + for (db::Cell::touching_iterator jj2 = cell.begin_touching (common.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { + std::vector p; + db::ICplxTrans t; + add_pair (i, p, t, *jj2, pp2, tt2); + } + } + + any = true; + + } + + } + + first = false; + + // we don't expect more to happen on the next instance + if (! any) { + break; + } + + } + } + /** * @brief Handles a local clusters vs. the clusters of a specific child instance or instance array * @param c1 The local cluster diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index b58f0460a..bb05fe3c4 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -1006,6 +1006,18 @@ TEST(110_HierClusters) TEST(111_HierClusters) { run_hc_test (_this, "hc_test_l11.gds", "hc_test_au11.gds"); - run_hc_test_with_backannotation (_this, "hc_test_l4.gds", "hc_test_au4b.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l11.gds", "hc_test_au11b.gds"); +} + +TEST(112_HierClusters) +{ + run_hc_test (_this, "hc_test_l12.gds", "hc_test_au12.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l12.gds", "hc_test_au12b.gds"); +} + +TEST(113_HierClusters) +{ + run_hc_test (_this, "hc_test_l13.gds", "hc_test_au13.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l13.gds", "hc_test_au13b.gds"); } diff --git a/testdata/algo/hc_test_au11b.gds b/testdata/algo/hc_test_au11b.gds new file mode 100644 index 0000000000000000000000000000000000000000..ee96f084217f7ba4acbbd6b073b37d9971d65889 GIT binary patch literal 14196 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 Dyt1%4 literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au12.gds b/testdata/algo/hc_test_au12.gds new file mode 100644 index 0000000000000000000000000000000000000000..72d2f1097a12a4ed1ab773620b17f30edb3b7f45 GIT binary patch literal 16598 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au12b.gds b/testdata/algo/hc_test_au12b.gds new file mode 100644 index 0000000000000000000000000000000000000000..e70c9617785f1ca6f4efc7bbb01f8f2e015f8d5e GIT binary patch literal 14128 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 Ue~33g~$6m_q$x1(B?*(58Uy5 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 K{_cj_+Vp?>N=F+2 literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au13b.gds b/testdata/algo/hc_test_au13b.gds new file mode 100644 index 0000000000000000000000000000000000000000..f14dfeaa9f78c00cb3173bb1fef46a7adc46c0df GIT binary patch literal 14996 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 qUH`E3#dRRp2vQ-{=as0b?Lvsm=pT| literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l12.gds b/testdata/algo/hc_test_l12.gds new file mode 100644 index 0000000000000000000000000000000000000000..17c1a90df13d271d4d751050f4ec46a145d30aa2 GIT binary patch literal 1008 zcmaizJ4gdT5QhJIZBCMik6IR$pE{AAeOBB8>7oOCs7Q`pgIcX9)0eO4WtQ{-+J1MnEL| z*dl7RBC6g9lKn52?5`0CpRR$f)rhJ$YCrsTyvoi!*K&<>X1a^)KZ~e3Grs?rpQt%U z^!6a++BJz@PnD_*ll(x+=lNkFz^4r9vQqVs@%T=ZKhblc-Gk?X-fo2rMsN%S}C z(l5@R>F-MYBdX4QP4IKSZ@At)ZN47^8SbT0b!O=Q=al-!l$Vxy8)?1`eR5*WpvB$E z1YWVil*jLvTarV-H^i!DfNWh!bhRFi!&tnA&uhr-5|76&@nEwG3>?9lx}?VQFb;kyh{z5KLibV_{)o zX(3h?A_x{1f{lfRkVK;f$JtAUyTbKg`Q5$a?as~{D7fwhwBu%;;lRTvMlhLvN9d~Z zN(m@Y+w9WC)bUZZ0v*_WN~y(Z_#vQjF7K@dTmZ$qN7Q+e1vtg@65SB~ zApZ7Ko#-bbqB`J>13^J3x}n|Q+9hgeBE!xeQM;W`^oF+kKW^AxBQk6~0DW5tMQ_w> zejsy8ba&R}JvXTTfJi7hGkt$I=^x+qg*&<3rC;Xrueeb?iq5q7iJHqqU$2JT`zF!n zrBHN3o1g0r`Tv9=z|(_lMJRgA^!Z*=KhgV~${&MV*L_c;)0BBAKa*q_0_QywZ+SzG68WO>eq%*3lPhtr94P^vP`6AqjC XFF@x#IGYiWI~8*7JMp)i<@@*rB9s$( literal 0 HcmV?d00001 From 72f838f8ee375e95dd66a9943026dd093a52f140 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 4 Jan 2019 01:22:24 +0100 Subject: [PATCH 171/335] WIP: performance improvement, maximum confinement of interactions by local search area. --- src/db/db/dbHierNetworkProcessor.cc | 44 ++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 6a87619f4..2c08701e1 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -882,7 +882,7 @@ public: { std::vector p; db::ICplxTrans t; - add_pair (*i1, p, t, *i2, p, t); + add_pair (box_type::world (), *i1, p, t, *i2, p, t); } /** @@ -955,7 +955,7 @@ private: * @param p2 The instantiation path to the child cell (not including i2) * @param t2 The accumulated transformation of the path, not including i2 */ - void add_pair (const db::Instance &i1, const std::vector &p1, const db::ICplxTrans &t1, const db::Instance &i2, const std::vector &p2, const db::ICplxTrans &t2) + void add_pair (const box_type &common, const db::Instance &i1, const std::vector &p1, const db::ICplxTrans &t1, const db::Instance &i2, const std::vector &p2, const db::ICplxTrans &t2) { box_type bb1 = (*mp_cbc) (i1.cell_index ()); box_type b1 = i1.cell_inst ().bbox (*mp_cbc).transformed (t1); @@ -963,16 +963,16 @@ private: box_type bb2 = (*mp_cbc) (i2.cell_index ()); box_type b2 = i2.cell_inst ().bbox (*mp_cbc).transformed (t2); - if (! b1.touches (b2)) { + box_type common_all = common & b1 & b2; + + if (common_all.empty ()) { return; } db::ICplxTrans t1i = t1.inverted (); db::ICplxTrans t2i = t2.inverted (); - box_type common = b1 & b2; - - for (db::CellInstArray::iterator ii1 = i1.begin_touching (common.transformed (t1i), mp_layout); ! ii1.at_end (); ++ii1) { + for (db::CellInstArray::iterator ii1 = i1.begin_touching (common_all.transformed (t1i), mp_layout); ! ii1.at_end (); ++ii1) { db::ICplxTrans tt1 = t1 * i1.complex_trans (*ii1); box_type ib1 = bb1.transformed (tt1); @@ -987,29 +987,37 @@ private: db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); box_type ib2 = bb2.transformed (tt2); - if (ib1.touches (ib2)) { + box_type common12 = ib1 & ib2 & common; + + if (! common12.empty ()) { std::vector pp2; pp2.reserve (p2.size () + 1); pp2.insert (pp2.end (), p2.begin (), p2.end ()); pp2.push_back (db::InstElement (i2, ii2)); - add_single_pair (common, i1.cell_index (), pp1, tt1, i2.cell_index (), pp2, tt2); + add_single_pair (common12, i1.cell_index (), pp1, tt1, i2.cell_index (), pp2, tt2); // dive into cell of ii2 const db::Cell &cell2 = mp_layout->cell (i2.cell_index ()); - for (db::Cell::touching_iterator jj2 = cell2.begin_touching (common.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { - add_pair (i1, p1, t1, *jj2, pp2, tt2); + for (db::Cell::touching_iterator jj2 = cell2.begin_touching (common12.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { + add_pair (common12, i1, p1, t1, *jj2, pp2, tt2); } } } - // dive into cell of ii1 - const db::Cell &cell1 = mp_layout->cell (i1.cell_index ()); - for (db::Cell::touching_iterator jj1 = cell1.begin_touching (common.transformed (tt1.inverted ())); ! jj1.at_end (); ++jj1) { - add_pair (*jj1, pp1, tt1, i2, p2, t2); + box_type common1 = ib1 & b2 & common; + + if (! common1.empty ()) { + + // dive into cell of ii1 + const db::Cell &cell1 = mp_layout->cell (i1.cell_index ()); + for (db::Cell::touching_iterator jj1 = cell1.begin_touching (common1.transformed (tt1.inverted ())); ! jj1.at_end (); ++jj1) { + add_pair (common1, *jj1, pp1, tt1, i2, p2, t2); + } + } } @@ -1129,7 +1137,7 @@ private: for (db::Cell::touching_iterator jj2 = cell.begin_touching (common.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { std::vector p; db::ICplxTrans t; - add_pair (i, p, t, *jj2, pp2, tt2); + add_pair (common, i, p, t, *jj2, pp2, tt2); } } @@ -1171,14 +1179,12 @@ private: return; } - box_type common = b1 & b2; - std::vector pp2; pp2.reserve (p2.size () + 1); pp2.insert (pp2.end (), p2.begin (), p2.end ()); pp2.push_back (db::InstElement ()); - for (db::CellInstArray::iterator ii2 = i2.begin_touching (common.transformed (t2.inverted ()), mp_layout); ! ii2.at_end (); ++ii2) { + for (db::CellInstArray::iterator ii2 = i2.begin_touching ((b1 & b2).transformed (t2.inverted ()), mp_layout); ! ii2.at_end (); ++ii2) { db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); box_type ib2 = bb2.transformed (tt2); @@ -1189,7 +1195,7 @@ private: add_single_pair (c1, i2.cell_index (), pp2, tt2); // dive into cell of ii2 - for (db::Cell::touching_iterator jj2 = cell2.begin_touching (common.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { + for (db::Cell::touching_iterator jj2 = cell2.begin_touching ((b1 & ib2).transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { add_pair (c1, *jj2, pp2, tt2); } From e439d50111f48a02d2f58fd51aa0713aa32d649d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 4 Jan 2019 08:03:31 +0100 Subject: [PATCH 172/335] WIP: hier netlist processort: performance improvement in instance-to-instance checking, leaner output of net shapes. --- src/db/db/dbHierNetworkProcessor.cc | 59 ++++++++++++++++++++++------- src/db/db/dbLayoutToNetlist.cc | 14 ++++++- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 2c08701e1..d80880951 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -330,6 +330,8 @@ template bool local_cluster::interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const { + db::box_convert bc; + const_cast *> (this)->ensure_sorted (); box_type common = other.bbox ().transformed (trans) & bbox (); @@ -339,35 +341,59 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans box_type common_for_other = common.transformed (trans.inverted ()); - db::box_scanner2 scanner; - transformed_box bc_t (trans); - db::box_convert bc; + // shortcut evaluation for disjunct layers - bool any = false; + std::set ll1; for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { - for (typename tree_type::touching_iterator i = s->second.begin_touching (common, bc); ! i.at_end (); ++i) { - scanner.insert1 (i.operator-> (), s->first); - any = true; + if (! s->second.begin_touching (common, bc).at_end ()) { + ll1.insert (s->first); } } + if (ll1.empty ()) { + return false; + } + + std::set ll2; + for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { + if (! s->second.begin_touching (common_for_other, bc).at_end ()) { + ll2.insert (s->first); + } + } + + if (ll2.empty ()) { + return false; + } + + bool any = false; + + for (std::set::const_iterator i = ll1.begin (); i != ll1.end () && !any; ++i) { + db::Connectivity::layer_iterator je = conn.end_connected (*i); + for (db::Connectivity::layer_iterator j = conn.begin_connected (*i); j != je && !any; ++j) { + any = (ll2.find (*j) != ll2.end ()); + } + } if (! any) { return false; } - any = false; + // detailed analysis + + db::box_scanner2 scanner; + transformed_box bc_t (trans); + + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + for (typename tree_type::touching_iterator i = s->second.begin_touching (common, bc); ! i.at_end (); ++i) { + scanner.insert1 (i.operator-> (), s->first); + } + } for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { for (typename tree_type::touching_iterator i = s->second.begin_touching (common_for_other, bc); ! i.at_end (); ++i) { scanner.insert2 (i.operator-> (), s->first); - any = true; } } - if (! any) { - return false; - } - interaction_receiver rec (conn, trans); return ! scanner.process (rec, 1 /*==touching*/, bc, bc_t); } @@ -1037,6 +1063,8 @@ private: db::cell_index_type ci1, const std::vector &p1, const db::ICplxTrans &t1, db::cell_index_type ci2, const std::vector &p2, const db::ICplxTrans &t2) { + const db::Cell &cell2 = mp_layout->cell (ci2); + const db::local_clusters &cl1 = mp_tree->clusters_per_cell (ci1); const db::local_clusters &cl2 = mp_tree->clusters_per_cell (ci2); @@ -1046,6 +1074,11 @@ private: for (typename db::local_clusters::touching_iterator i = cl1.begin_touching (common.transformed (t1i)); ! i.at_end (); ++i) { + // skip the test, if this cluster doesn't interact with the whole cell2 + if (! i->interacts (cell2, t21, *mp_conn)) { + continue; + } + box_type bc1 = common & i->bbox ().transformed (t1); for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (bc1.transformed (t2i)); ! j.at_end (); ++j) { diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index b8491b9ac..ab6895ef2 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -209,6 +209,16 @@ const db::hier_clusters &LayoutToNetlist::net_clusters () const return m_netex.clusters (); } +template +static void deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr) +{ + if (pr.obj ().is_box ()) { + region.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); + } else { + region.insert (pr.obj ().transformed (pr.trans ()).transformed (tr)); + } +} + db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const { unsigned int lid = layer_of (of_layer); @@ -224,7 +234,7 @@ db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region const db::local_cluster &lc = m_netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); for (db::local_cluster::shape_iterator s = lc.begin (lid); !s.at_end (); ++s) { - res->insert (s->obj ().transformed (s->trans ())); + deliver_shape (*s, *res, db::UnitTrans ()); } } else { @@ -235,7 +245,7 @@ db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region db::cell_index_type ci = circuit->cell_index (); for (db::recursive_cluster_shape_iterator rci (m_netex.clusters (), lid, ci, net.cluster_id ()); !rci.at_end (); ++rci) { - res->insert (rci->obj ().transformed (rci.trans () * db::ICplxTrans (rci->trans ()))); + deliver_shape (*rci, *res, rci.trans ()); } } From ad6d9b5715b84fd989976da192253fdaafe49666 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 4 Jan 2019 17:41:09 +0100 Subject: [PATCH 173/335] WIP: provide a less memory intensive way to deliver shapes from nets. --- src/db/db/dbLayoutToNetlist.cc | 78 +++++++++++++++------ src/db/db/dbLayoutToNetlist.h | 11 +++ src/db/db/gsiDeclDbLayoutToNetlist.cc | 7 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 30 ++++---- testdata/ruby/dbLayoutToNetlist.rb | 6 ++ 5 files changed, 95 insertions(+), 37 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index ab6895ef2..d9c3052b4 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -219,35 +219,69 @@ static void deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const T } } +template +static void deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const Tr &tr) +{ + if (pr.obj ().is_box ()) { + shapes.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); + } else { + db::Layout *layout = shapes.layout (); + if (layout) { + shapes.insert (db::PolygonRef (pr.obj ().transformed (pr.trans ()).transformed (tr), layout->shape_repository ())); + } else { + shapes.insert (pr.obj ().transformed (pr.trans ()).transformed (tr)); + } + } +} + +template +static void deliver_shapes_of_net_recursive (const db::NetlistExtractor &netex, const db::Net &net, unsigned int layer_id, To &to) +{ + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + db::cell_index_type ci = circuit->cell_index (); + + for (db::recursive_cluster_shape_iterator rci (netex.clusters (), layer_id, ci, net.cluster_id ()); !rci.at_end (); ++rci) { + deliver_shape (*rci, to, rci.trans ()); + } +} + +template +static void deliver_shapes_of_net_nonrecursive (const db::NetlistExtractor &netex, const db::Net &net, unsigned int layer_id, To &to) +{ + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + db::cell_index_type ci = circuit->cell_index (); + + const db::local_cluster &lc = netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); + + for (db::local_cluster::shape_iterator s = lc.begin (layer_id); !s.at_end (); ++s) { + deliver_shape (*s, to, db::UnitTrans ()); + } +} + +void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const +{ + unsigned int lid = layer_of (of_layer); + + if (! recursive) { + deliver_shapes_of_net_nonrecursive (m_netex, net, lid, to); + } else { + deliver_shapes_of_net_recursive (m_netex, net, lid, to); + } +} + db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const { unsigned int lid = layer_of (of_layer); std::auto_ptr res (new db::Region ()); if (! recursive) { - - const db::Circuit *circuit = net.circuit (); - tl_assert (circuit != 0); - - db::cell_index_type ci = circuit->cell_index (); - - const db::local_cluster &lc = m_netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); - - for (db::local_cluster::shape_iterator s = lc.begin (lid); !s.at_end (); ++s) { - deliver_shape (*s, *res, db::UnitTrans ()); - } - + deliver_shapes_of_net_nonrecursive (m_netex, net, lid, *res); } else { - - const db::Circuit *circuit = net.circuit (); - tl_assert (circuit != 0); - - db::cell_index_type ci = circuit->cell_index (); - - for (db::recursive_cluster_shape_iterator rci (m_netex.clusters (), lid, ci, net.cluster_id ()); !rci.at_end (); ++rci) { - deliver_shape (*rci, *res, rci.trans ()); - } - + deliver_shapes_of_net_recursive (m_netex, net, lid, *res); } return res.release (); diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 09834dcf1..4efaf4294 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -224,6 +224,17 @@ public: */ db::Region *shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const; + /** + * @brief Delivers all shapes of a specific net and layer to the given Shapes container. + * + * If "recursive" is true, the returned region will contain the shapes of + * all subcircuits too. + * + * This methods returns a new'd Region. It's the responsibility of the caller + * to delete this object. + */ + void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const; + /** * @brief Finds the net by probing a specific location on the given layer * diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 6cee2d264..1e47fddc8 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -141,11 +141,16 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("netlist", &db::LayoutToNetlist::netlist, "@brief gets the netlist extracted (0 if no extraction happened yet)\n" ) + - gsi::factory ("shapes_of_net", &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), + gsi::factory ("shapes_of_net", (db::Region *(db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), "@brief Returns all shapes of a specific net and layer.\n" "If 'recursive'' is true, the returned region will contain the shapes of\n" "all subcircuits too.\n" ) + + gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, db::Shapes &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"), + "@brief Sends all shapes of a specific net and layer to the given Shapes container.\n" + "If 'recursive'' is true, the returned region will contain the shapes of\n" + "all subcircuits too.\n" + ) + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), "@brief Finds the net by probing a specific location on the given layer\n" "\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index e70ab822c..b1ace3ea0 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -148,23 +148,25 @@ static void dump_recursive_nets_to_layout (const db::LayoutToNetlist &l2n, db::L continue; } + bool any = false; + for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any; ++m) { + any = !db::recursive_cluster_shape_iterator (l2n.net_clusters (), l2n.layer_of (*m->first), c->cell_index (), n->cluster_id ()).at_end (); + } + + if (!any) { + continue; + } + db::cell_index_type nci = std::numeric_limits::max (); + if (nci == std::numeric_limits::max ()) { + std::string nn = "RNET_" + c->name () + "_" + n->expanded_name (); + nci = ly.add_cell (nn.c_str ()); + cell.insert (db::CellInstArray (db::CellInst (nci), db::Trans ())); + } + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { - - std::auto_ptr shapes (l2n.shapes_of_net (*n, *m->first, true)); - if (shapes->empty ()) { - continue; - } - - if (nci == std::numeric_limits::max ()) { - std::string nn = "RNET_" + c->name () + "_" + n->expanded_name (); - nci = ly.add_cell (nn.c_str ()); - cell.insert (db::CellInstArray (db::CellInst (nci), db::Trans ())); - } - - shapes->insert_into (&ly, nci, m->second); - + l2n.shapes_of_net (*n, *m->first, true, ly.cell (nci).shapes (m->second)); } } diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index 59d821ab8..d1a37ef11 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -120,6 +120,12 @@ END assert_equal(l2n.shapes_of_net(n, rmetal1, true).to_s, "(1660,-420;1660,2420;2020,2420;2020,-420);(1840,820;1840,1180;3220,1180;3220,820);(1660,2420;1660,3180;2020,3180;2020,2420);(1660,-380;1660,380;2020,380;2020,-380)") + shapes = RBA::Shapes::new + l2n.shapes_of_net(n, rmetal1, true, shapes) + r = RBA::Region::new + shapes.each { |s| r.insert(s.polygon) } + assert_equal(r.to_s, "(1660,-420;1660,2420;2020,2420;2020,-420);(1840,820;1840,1180;3220,1180;3220,820);(1660,2420;1660,3180;2020,3180;2020,2420);(1660,-380;1660,380;2020,380;2020,-380)") + end def test_10_LayoutToNetlistExtractionWithoutDevices From c31c87916c74eae7ea7fb46bb357554e3954084a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Jan 2019 01:34:10 +0100 Subject: [PATCH 174/335] WIP: bugfix - array reference were not always considered correctly. --- src/db/db/dbHierNetworkProcessor.cc | 2 +- src/db/unit_tests/dbHierNetworkProcessorTests.cc | 6 ++++++ testdata/algo/hc_test_au14.gds | Bin 0 -> 2062 bytes testdata/algo/hc_test_au14b.gds | Bin 0 -> 1794 bytes testdata/algo/hc_test_l14.gds | Bin 0 -> 386 bytes 5 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 testdata/algo/hc_test_au14.gds create mode 100644 testdata/algo/hc_test_au14b.gds create mode 100644 testdata/algo/hc_test_l14.gds diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index d80880951..e0f1a447b 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1206,7 +1206,7 @@ private: db::ICplxTrans t2i = t2 * i2.complex_trans (); const db::Cell &cell2 = mp_layout->cell (i2.cell_index ()); - box_type b2 = cell2.bbox ().transformed (t2i); + box_type b2 = i2.cell_inst ().bbox (*mp_cbc).transformed (t2); if (! b1.touches (b2) || ! c1.interacts (cell2, t2i, *mp_conn)) { return; diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index bb05fe3c4..c376f26f1 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -1021,3 +1021,9 @@ TEST(113_HierClusters) run_hc_test_with_backannotation (_this, "hc_test_l13.gds", "hc_test_au13b.gds"); } +TEST(114_HierClusters) +{ + run_hc_test (_this, "hc_test_l14.gds", "hc_test_au14.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l14.gds", "hc_test_au14b.gds"); +} + diff --git a/testdata/algo/hc_test_au14.gds b/testdata/algo/hc_test_au14.gds new file mode 100644 index 0000000000000000000000000000000000000000..7f5500e605e61921b25affa7167bb3a25a6777b0 GIT binary patch literal 2062 zcma)-v1=4T6vn@u-J4qsN{lH|93nwb@j%2vg{Wu}h!`|QijY4*>`WJ1J1qnorXiV-SIqx z-%BCl+oKMn+T}sW3Uf6jNy&}a6Bl#%BOV=%4aj(pc6rb3o)ZFUz=~?n3>GN6ol=Q0B zO^^pb?107c-MybT-`p1e!Na*V^fTA{jtpIUj$yv} z8HMw=Zd?OqJKf7ypPs+@@y)M`OQSQdU#_pB-)--C?-$P^F93hg*}49Z18{n}6T z_wmV^%2R&!=ppH4UsRs*uZ&7RN&os>P5P^O>+k2B9+F=6Mdc~~sbT3S=|5N0q`#WC z`Z=eEq?dhBdCKnwrJtmKYOf~!)x6cuIXxu3?2F11etdlr{gLL!#2(T7nz#Am+|G|Q zZ|sZ8Q~r04Ns66H8{-TGZpZiNxZfyED>ix&Pt39i|D=PnI|K4l% M|7ZW&KTDqDFNTM5-v9sr literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l14.gds b/testdata/algo/hc_test_l14.gds new file mode 100644 index 0000000000000000000000000000000000000000..3b1eaa46fa62cb8ac0ab3de7ac80fd82e83b58b1 GIT binary patch literal 386 zcmaKnu}T9$6h+Uzoqf9~N}?$&wh*xt#7cxHXwry6QltoeMSjE1MhGZkXKU#Phy;uH z5#|G|f}rr6iH>XvErv7i4)@+SC?b8zMDkY>GJn>oSML;}r^iRoNV9{(^WBrDySKge z!}jgfS&uUN+o!6n9b5srBtBP{1=y;kW*=R%&$U3v8sP3DqaKK>`=^|LTME@)$H%xu z-?I`Li&bF(p{%9QdWVfilIP)!vw1N2PX5_Us+)~RW|Owrcm$W%$o?QQ>Vb3qwCbNW W&0a3drcJmRBKc-y)B}Hi<{V%4SY9ds literal 0 HcmV?d00001 From 6e468b43e0ae2b0b79e2fae7e17e25daaac74265 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Jan 2019 10:12:55 +0100 Subject: [PATCH 175/335] WIP: bugfix - local to instance interaction did shortcut too early. --- src/db/db/dbHierNetworkProcessor.cc | 5 ++--- src/db/unit_tests/dbHierNetworkProcessorTests.cc | 6 ++++++ testdata/algo/hc_test_au15.gds | Bin 0 -> 3198 bytes testdata/algo/hc_test_au15b.gds | Bin 0 -> 2818 bytes testdata/algo/hc_test_l15.gds | Bin 0 -> 898 bytes 5 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 testdata/algo/hc_test_au15.gds create mode 100644 testdata/algo/hc_test_au15b.gds create mode 100644 testdata/algo/hc_test_l15.gds diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index e0f1a447b..155e76e92 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1203,12 +1203,11 @@ private: box_type bb2 = (*mp_cbc) (i2.cell_index ()); - db::ICplxTrans t2i = t2 * i2.complex_trans (); const db::Cell &cell2 = mp_layout->cell (i2.cell_index ()); box_type b2 = i2.cell_inst ().bbox (*mp_cbc).transformed (t2); - if (! b1.touches (b2) || ! c1.interacts (cell2, t2i, *mp_conn)) { + if (! b1.touches (b2)) { return; } @@ -1222,7 +1221,7 @@ private: db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); box_type ib2 = bb2.transformed (tt2); - if (b1.touches (ib2)) { + if (b1.touches (ib2) && c1.interacts (cell2, tt2, *mp_conn)) { pp2.back () = db::InstElement (i2, ii2); add_single_pair (c1, i2.cell_index (), pp2, tt2); diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index c376f26f1..a16a73294 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -1027,3 +1027,9 @@ TEST(114_HierClusters) run_hc_test_with_backannotation (_this, "hc_test_l14.gds", "hc_test_au14b.gds"); } +TEST(115_HierClusters) +{ + run_hc_test (_this, "hc_test_l15.gds", "hc_test_au15.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l15.gds", "hc_test_au15b.gds"); +} + diff --git a/testdata/algo/hc_test_au15.gds b/testdata/algo/hc_test_au15.gds new file mode 100644 index 0000000000000000000000000000000000000000..15cd28050d2a46835307605d547b1d2a861d6dff GIT binary patch literal 3198 zcma);Pe>I}6vn?h?>P=-VJWnE&_AK52NJv}rG(Nz(@5eG6+s1Uf)+0baw!C1`v@V5 z7J)7bTC{NC(jr>5aN({+F08CvxDXQTe0SzB_wl^LMdYX7edn9=op(9rCl^{ToRXkByuMx=U>X=boM#{WP=IH#FD#`o)bpDsBF{IF1KTpqv2lu1LJ& z-4TFlXK8C|{jL529h2^_>Lkj0fO0zuuad-Z{qnecZl_#lFRz$%MUo*xrLzPKZUBcC zV7^RS+W#BNsdLs@ly?T4tpi7vQeKz>Miv33>O$MucJ=orfFAK6hi8F?q*JOc^wvM? zNIy?rng`~GbJ8bU_troE3>=iKWfAsxI`_PgK+X$v&CG<4R8UpRGIfrJwHn0{f4T^NvcY+1}@UlGyvK)lIwB z6iU^Ffo_4>0^Kfl0|S#Vj4{1u0 z=^jnxWXf#aH~$tdreO`!(Mzd%LCQaF%D~gqpR&pA-C~-OWXg}TIhitB_oZy|da{su zk@R{cXSR}F*}Bi`hntdTy`Jp_XTCpWliR;_doO{MxtlI=S91Q@dQfMJ%c&R1?{IZ4 zC%v-uATLgrH6*<_eWmKcATLgrH6%SaeWmIJUMueO1D}}w>AP;B&(A=&!)3e63@N+I z^e^RG{Z)rJ1)H@QvM(&>{-_ddxf9hq$1H~khbPMI~FI=z&t7fgT6P06Pu kuTQcsJ}s517fkH0>&2%xuTQccKE;))Z`YT+)tAfo4=%sW!vFvP literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au15b.gds b/testdata/algo/hc_test_au15b.gds new file mode 100644 index 0000000000000000000000000000000000000000..bfde53a73b21f1db19f5ee4453d9027631890ba1 GIT binary patch literal 2818 zcmbW3Pe@cz6vn^%W_%81VJWm3=$}x8Ac0Xz38jIik;D)cK?VIGXfZ*MOCbo`hX_%$ z2y{`%gH! zxGyuF{_px~vU7T}C_5Q&rUo2YNqBw=7+D5H#f|=C|I$C00J>}sa(E7?TXmx1#-RR1 zTe6j_`+J%{#_c&4%~$zCz3uPUnZMVIiWm9c{FwNY^nEgwk^X5u z=+8WTNcvJQDqiIOemU_c>HldiBmL8S(Er(Dq9>_;ZU2iR^=Uq+|Gk!+pR{wkj-`w| zf0_^Kmsb-#$+~~eXQV#O2lcFv>qzQZzo@t|sAqj#M^eZ7Ma2vB?r_=n&(r$m`d57a zJgtAqhx~os_V+YjuM-t7@b~@Y_ryNapZymVH-`Luf4Po*=J(G2i;5TM-Ar#%KS|#Y zm5ioSKIHH7tdn}yO}(giLH+*Tpbzg4`t$w}6)!qJ?=AX}^yU2}D!#@4Uhe*DX8tzl zo0z{1`X=UYgMQkc7tPsi&ff+-=dR}LHs|mCT>t#s)11@X6BXYw|Nhcz&S~z6if@^J Y4f-bLUxU7h_lE}k|GYogn`OC-KZaVKfB*mh literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l15.gds b/testdata/algo/hc_test_l15.gds new file mode 100644 index 0000000000000000000000000000000000000000..ff5f9ece9558ed08204916f020aec470c98760c4 GIT binary patch literal 898 zcmaKqF-yZh6vzK}Nv}bxRPC5SrJzVbgo;E|&{k+^#UdS492{H(hYk)M3og2g2qL)E z!4J@(qlh>;IJi0J2XJt3DAadJ9?9T!NPfxx@_YAQI4DTD3s=bGCj=50qI)L(3`eSJ zc>~B;iM92MrOH$L+beeF?{4<%a1&-vDOFg2?Eq>-QdKo70p!wFzyI^V7m#YjFU~b# zj{$ZP19x$hs&CimyJ6aAT<_>iNSZ>zOu%3)_R>I_(|12gXd zH_w6c8^Gk8=y&#?f7}GJR710L4>V{xCg((>f0+ut*WBs?-BLtdCtNrBSI@u{&8j)O z0^yuS$s5SrB- zM4Rd{IVT$Z9rC;r2+hh7#2y_DCg;R~e!d&{%RPt|_08m*X!M)8pnpw|{==CYQTHFN K`#zm)+r}TDZHQI? literal 0 HcmV?d00001 From 15b79c9ddb80dae4500cb43c19d965988d2cc571 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Jan 2019 21:55:06 +0100 Subject: [PATCH 176/335] IMPORTANT BUGFIX: array repo handling The issue: when cloning an array, the "in_repository" flag might be left set. This makes the system think the array is kept in a repo. In the best case this creates a memory leak. --- src/db/db/dbArray.h | 11 +++++++++++ src/db/db/dbCellMapping.cc | 2 +- src/db/unit_tests/dbCellMapping.cc | 26 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 1474e92f3..b2e8cdff4 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -87,6 +87,17 @@ struct ArrayBase // .. nothing yet .. } + ArrayBase (const ArrayBase &) + : in_repository (false) + { + // .. nothing yet .. + } + + ArrayBase &operator= (const ArrayBase &) + { + return *this; + } + virtual ~ArrayBase () { // .. nothing yet .. diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index 4c403c8c1..0bfc9c69c 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -323,7 +323,7 @@ CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type / db::CellInstArray bci = bi.cell_inst (); bci.object ().cell_index (new_cells [i]); - bci.transform_into (db::ICplxTrans (mag)); + bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ()); if (bi.has_prop_id ()) { pa.insert (db::CellInstArrayWithProperties (bci, pm (bi.prop_id ()))); diff --git a/src/db/unit_tests/dbCellMapping.cc b/src/db/unit_tests/dbCellMapping.cc index c9d2ea7ea..0d346e0a6 100644 --- a/src/db/unit_tests/dbCellMapping.cc +++ b/src/db/unit_tests/dbCellMapping.cc @@ -427,3 +427,29 @@ TEST(5) EXPECT_EQ (l2s (hh), "a0top#0:cell_index=4 r90 0,0 array=(0,10,10,0 5x1),cell_index=5 r90 0,0 array=(0,10,10,0 5x2),cell_index=1 r0 10,0,cell_index=6 r90 0,0 array=(0,20,20,0 5x2),cell_index=7 r90 0,0 array=(0,20,20,0 5x2);a1#1:cell_index=2 r0 0,0,cell_index=2 r0 0,20,cell_index=3 r0 0,40;a2#2:cell_index=3 r0 0,0,cell_index=3 r0 0,10;a3#3:cell_index=4 r90 0,0;a4#4:;a5#5:;a4$1#6:;a5$1#7:"); } +// Resolution of array references +TEST(6) +{ + std::auto_ptr g (new db::Layout ()); + db::Cell &a0 (g->cell (g->add_cell ("a0"))); + db::Cell *a4p, *a5p; + a4p = &(g->cell (g->add_cell ("a4"))); + a5p = &(g->cell (g->add_cell ("a5"))); + db::Cell &a4 (*a4p); + db::Cell &a5 (*a5p); + + a0.insert (db::CellInstArray (db::CellInst (a4.cell_index ()), db::Trans (1/*r90*/), g->array_repository (), db::Vector(0, 10), db::Vector(10, 0), 5, 2)); + a0.insert (db::CellInstArray (db::CellInst (a5.cell_index ()), db::Trans (1/*r90*/), g->array_repository (), db::Vector(0, 10), db::Vector(10, 0), 5, 2)); + + db::Layout h; + db::Cell &b0 (h.cell (h.add_cell ("a0top"))); + + db::CellMapping cm; + cm.create_single_mapping_full (h, b0.cell_index (), *g, a0.cell_index ()); + EXPECT_EQ (m2s (cm, *g, h), "a0->a0top;a4->a4;a5->a5"); + + g.reset (0); + + EXPECT_EQ (l2s (h), "a0top#0:cell_index=1 r90 0,0 array=(0,10,10,0 5x2),cell_index=2 r90 0,0 array=(0,10,10,0 5x2);a4#1:;a5#2:"); +} + From cbca1fb5308e135f44b5384c619b37a4ce05558b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Jan 2019 22:39:52 +0100 Subject: [PATCH 177/335] Cleaned up a namespace mess (db::Net was duplicated in NetTracer and db itself) - renamed to db::NetTracerNet, but better solution would be to rename db to something else in the plugin. --- .../net_tracer/db_plugin/dbNetTracerIO.cc | 14 ++++----- .../net_tracer/db_plugin/dbNetTracerIO.h | 6 ++-- .../lay_plugin/layNetTracerDialog.cc | 30 +++++++++---------- .../lay_plugin/layNetTracerDialog.h | 6 ++-- .../net_tracer/unit_tests/dbNetTracer.cc | 12 ++++---- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc index 2e52c70ae..bb44ab5bc 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc @@ -338,13 +338,13 @@ NetTracerSymbolInfo::parse (tl::Extractor &ex) // ----------------------------------------------------------------------------------- // Net implementation -Net::Net () +NetTracerNet::NetTracerNet () : m_dbu (0.001), m_incomplete (true), m_trace_path (false) { // .. nothing yet .. } -Net::Net (const NetTracer &tracer, const db::ICplxTrans &trans, const db::Layout &layout, db::cell_index_type cell_index, const std::string &layout_filename, const std::string &layout_name, const NetTracerData &data) +NetTracerNet::NetTracerNet (const NetTracer &tracer, const db::ICplxTrans &trans, const db::Layout &layout, db::cell_index_type cell_index, const std::string &layout_filename, const std::string &layout_name, const NetTracerData &data) : m_name (tracer.name ()), m_incomplete (tracer.incomplete ()), m_trace_path (false) { m_dbu = layout.dbu (); @@ -406,7 +406,7 @@ Net::Net (const NetTracer &tracer, const db::ICplxTrans &trans, const db::Layout } std::vector -Net::export_net (db::Layout &layout, db::Cell &export_cell) +NetTracerNet::export_net (db::Layout &layout, db::Cell &export_cell) { std::vector new_layers; std::map layer_map; @@ -446,7 +446,7 @@ Net::export_net (db::Layout &layout, db::Cell &export_cell) } const std::string & -Net::cell_name (db::cell_index_type cell_index) const +NetTracerNet::cell_name (db::cell_index_type cell_index) const { std::map ::const_iterator cn = m_cell_names.find (cell_index); if (cn != m_cell_names.end ()) { @@ -458,7 +458,7 @@ Net::cell_name (db::cell_index_type cell_index) const } db::LayerProperties -Net::representative_layer_for (unsigned int log_layer) const +NetTracerNet::representative_layer_for (unsigned int log_layer) const { std::map >::const_iterator l = m_layers.find (log_layer); if (l != m_layers.end ()) { @@ -469,7 +469,7 @@ Net::representative_layer_for (unsigned int log_layer) const } db::LayerProperties -Net::layer_for (unsigned int log_layer) const +NetTracerNet::layer_for (unsigned int log_layer) const { std::map >::const_iterator l = m_layers.find (log_layer); if (l != m_layers.end ()) { @@ -480,7 +480,7 @@ Net::layer_for (unsigned int log_layer) const } void -Net::define_layer (unsigned int l, const db::LayerProperties &lp, const db::LayerProperties &lp_representative) +NetTracerNet::define_layer (unsigned int l, const db::LayerProperties &lp, const db::LayerProperties &lp_representative) { m_layers.insert (std::make_pair (l, std::make_pair (lp, lp_representative))); } diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h index 2553cf16d..31e023580 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h @@ -157,7 +157,7 @@ private: std::string m_expression; }; -class DB_PLUGIN_PUBLIC Net +class DB_PLUGIN_PUBLIC NetTracerNet { public: typedef std::vector ::const_iterator iterator; @@ -165,12 +165,12 @@ public: /** * @brief Default constructor */ - Net (); + NetTracerNet (); /** * @brief Constructor */ - Net (const db::NetTracer &tracer, const db::ICplxTrans &trans, const db::Layout &layout, db::cell_index_type cell_index, const std::string &layout_filename, const std::string &layout_name, const db::NetTracerData &data); + NetTracerNet (const db::NetTracer &tracer, const db::ICplxTrans &trans, const db::Layout &layout, db::cell_index_type cell_index, const std::string &layout_filename, const std::string &layout_name, const db::NetTracerData &data); /** * @brief Iterate the shapes (begin) diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc index 18d07bad0..dadf49334 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc @@ -103,7 +103,7 @@ NetTracerDialog::~NetTracerDialog () void NetTracerDialog::clear_nets () { - for (std::vector ::iterator n = mp_nets.begin (); n != mp_nets.end (); ++n) { + for (std::vector ::iterator n = mp_nets.begin (); n != mp_nets.end (); ++n) { delete *n; } mp_nets.clear (); @@ -178,7 +178,7 @@ NetTracerDialog::mouse_click_event (const db::DPoint &p, unsigned int buttons, b stop_search_box = db::DBox (m_mouse_first_point, m_mouse_first_point).enlarged (db::DVector (l, l)); } - db::Net *net = do_trace (start_search_box, stop_search_box, trace_path); + db::NetTracerNet *net = do_trace (start_search_box, stop_search_box, trace_path); if (net) { // create a new net taking the shapes from the tracer @@ -220,7 +220,7 @@ NetTracerDialog::redo_trace_clicked () { BEGIN_PROTECTED - std::set selected_nets; + std::set selected_nets; QList selected_items = net_list->selectedItems (); for (QList::const_iterator item = selected_items.begin (); item != selected_items.end (); ++item) { @@ -230,18 +230,18 @@ BEGIN_PROTECTED } } - std::vector nets; + std::vector nets; nets.swap (mp_nets); m_net_index = 1; std::vector new_selection; - for (std::vector ::const_iterator n = nets.begin (); n != nets.end (); ++n) { + for (std::vector ::const_iterator n = nets.begin (); n != nets.end (); ++n) { try { - db::Net *net = do_trace ((*n)->start_search_box (), (*n)->stop_search_box (), (*n)->trace_path_flag ()); + db::NetTracerNet *net = do_trace ((*n)->start_search_box (), (*n)->stop_search_box (), (*n)->trace_path_flag ()); if (net) { // create a new net taking the shapes from the tracer @@ -281,7 +281,7 @@ BEGIN_PROTECTED END_PROTECTED } -db::Net * +db::NetTracerNet * NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &stop_search_box, bool trace_path) { unsigned int start_layer = 0; @@ -401,7 +401,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto } else { // create a new net taking the shapes from the tracer - db::Net *net = new db::Net (net_tracer, db::ICplxTrans (cv.context_trans ()), cv->layout (), cv.cell_index (), cv->filename (), cv->name (), tracer_data); + db::NetTracerNet *net = new db::NetTracerNet (net_tracer, db::ICplxTrans (cv.context_trans ()), cv->layout (), cv.cell_index (), cv->filename (), cv->name (), tracer_data); net->set_start_search_box (start_search_box); net->set_stop_search_box (stop_search_box); net->set_trace_path_flag (trace_path); @@ -712,7 +712,7 @@ NetTracerDialog::update_info () std::map::perimeter_type> statinfo_perimeter; size_t tot_shapes = 0; - for (db::Net::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { + for (db::NetTracerNet::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { if (tot_shapes++ >= max_shapes) { incomplete = true; @@ -917,7 +917,7 @@ NetTracerDialog::update_info () bool incomplete = false; std::set labels; - for (db::Net::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { + for (db::NetTracerNet::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { if (net_shape->shape ().is_text ()) { @@ -962,7 +962,7 @@ NetTracerDialog::update_info () incomplete = false; std::set cells; - for (db::Net::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { + for (db::NetTracerNet::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { if (cells.size () >= max_cells) { incomplete = true; @@ -1238,7 +1238,7 @@ BEGIN_PROTECTED w.start_element ("net"); - const db::Net *net = mp_nets[item_index]; + const db::NetTracerNet *net = mp_nets[item_index]; w.start_element ("name"); w.cdata (net->name ()); @@ -1262,7 +1262,7 @@ BEGIN_PROTECTED w.start_element ("shapes"); - for (db::Net::iterator net_shape = net->begin (); net_shape != net->end (); ++net_shape) { + for (db::NetTracerNet::iterator net_shape = net->begin (); net_shape != net->end (); ++net_shape) { w.start_element ("element"); @@ -1443,7 +1443,7 @@ NetTracerDialog::adjust_view () db::DBox cv_bbox; // Create markers for the shapes - for (db::Net::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { + for (db::NetTracerNet::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { // Find the actual layer by looking up the layer properties .. std::map ::const_iterator ll = llmap.find (net_shape->layer ()); @@ -1534,7 +1534,7 @@ NetTracerDialog::update_highlights () QColor net_color = mp_nets [item_index]->color (); // Create markers for the shapes - for (db::Net::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end () && n_marker < m_max_marker_count; ++net_shape) { + for (db::NetTracerNet::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end () && n_marker < m_max_marker_count; ++net_shape) { // Find the actual layer by looking up the layer properties .. std::map ::const_iterator ll = llmap.find (net_shape->layer ()); diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h index 3198b52e9..43da7ecda 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h @@ -38,7 +38,7 @@ namespace db { - class Net; + class NetTracerNet; } namespace lay @@ -82,7 +82,7 @@ protected slots: void redo_trace_clicked (); private: - std::vector mp_nets; + std::vector mp_nets; std::vector mp_markers; unsigned int m_cv_index; int m_net_index; @@ -113,7 +113,7 @@ private: void update_info (); void layer_list_changed (int index); void release_mouse (); - db::Net *do_trace (const db::DBox &start_search_box, const db::DBox &stop_search_box, bool trace_path); + db::NetTracerNet *do_trace (const db::DBox &start_search_box, const db::DBox &stop_search_box, bool trace_path); }; } diff --git a/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc b/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc index fdcc08a02..47e42d701 100644 --- a/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc +++ b/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc @@ -76,18 +76,18 @@ static db::NetTracerShape find_shape (const db::Layout &layout, const db::Cell & } #endif -static db::Net trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerTechnologyComponent &tc, unsigned int l_start, const db::Point &p_start) +static db::NetTracerNet trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerTechnologyComponent &tc, unsigned int l_start, const db::Point &p_start) { db::NetTracerData tracer_data = tc.get_tracer_data (layout); tracer.trace (layout, cell, p_start, l_start, tracer_data); - return db::Net (tracer, db::ICplxTrans (), layout, cell.cell_index (), std::string (), std::string (), tracer_data); + return db::NetTracerNet (tracer, db::ICplxTrans (), layout, cell.cell_index (), std::string (), std::string (), tracer_data); } -static db::Net trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerTechnologyComponent &tc, unsigned int l_start, const db::Point &p_start, unsigned int l_stop, const db::Point &p_stop) +static db::NetTracerNet trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerTechnologyComponent &tc, unsigned int l_start, const db::Point &p_start, unsigned int l_stop, const db::Point &p_stop) { db::NetTracerData tracer_data = tc.get_tracer_data (layout); tracer.trace (layout, cell, p_start, l_start, p_stop, l_stop, tracer_data); - return db::Net (tracer, db::ICplxTrans (), layout, cell.cell_index (), std::string (), std::string (), tracer_data); + return db::NetTracerNet (tracer, db::ICplxTrans (), layout, cell.cell_index (), std::string (), std::string (), tracer_data); } void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracerTechnologyComponent &tc, const db::LayerProperties &lp_start, const db::Point &p_start, const std::string &file_au, const char *net_name = 0) @@ -107,7 +107,7 @@ void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracer const db::Cell &cell = layout_org.cell (*layout_org.begin_top_down ()); db::NetTracer tracer; - db::Net net = trace (tracer, layout_org, cell, tc, layer_for (layout_org, lp_start), p_start); + db::NetTracerNet net = trace (tracer, layout_org, cell, tc, layer_for (layout_org, lp_start), p_start); if (net_name) { EXPECT_EQ (net.name (), std::string (net_name)); @@ -141,7 +141,7 @@ void run_test2 (tl::TestBase *_this, const std::string &file, const db::NetTrace const db::Cell &cell = layout_org.cell (*layout_org.begin_top_down ()); db::NetTracer tracer; - db::Net net = trace (tracer, layout_org, cell, tc, layer_for (layout_org, lp_start), p_start, layer_for (layout_org, lp_stop), p_stop); + db::NetTracerNet net = trace (tracer, layout_org, cell, tc, layer_for (layout_org, lp_start), p_start, layer_for (layout_org, lp_stop), p_stop); if (net_name) { EXPECT_EQ (net.name (), std::string (net_name)); From f86f8149eb2da25e0d8286602832e40f3030755e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Jan 2019 22:40:53 +0100 Subject: [PATCH 178/335] Fixed a bug that caused a segfault in the Layout2Netlist object (array repo references to original layout rather than to the working layout) --- src/db/db/dbHierarchyBuilder.cc | 2 +- src/db/unit_tests/dbHierarchyBuilderTests.cc | 37 +++++++++++++++++++ testdata/algo/hierarchy_builder_au_l5.gds | Bin 0 -> 3566 bytes testdata/algo/hierarchy_builder_l5.gds | Bin 0 -> 3566 bytes testdata/algo/hierarchy_builder_l5.oas.gz | Bin 0 -> 497 bytes 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 testdata/algo/hierarchy_builder_au_l5.gds create mode 100644 testdata/algo/hierarchy_builder_l5.gds create mode 100644 testdata/algo/hierarchy_builder_l5.oas.gz diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 88039a4b4..5dc23833d 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -242,7 +242,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn // for new cells, create this instance if (m_cell_stack.back ().first) { - db::CellInstArray new_inst = inst; + db::CellInstArray new_inst (inst, &mp_target->array_repository ()); new_inst.object () = db::CellInst (m_cm_entry->second); m_cell_stack.back ().second->insert (new_inst); } diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc index 317b5883f..54bc333d0 100644 --- a/src/db/unit_tests/dbHierarchyBuilderTests.cc +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -498,3 +498,40 @@ TEST(6_DisjunctLayersPerHierarchyBranch) db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au_l4.gds"); } +TEST(7_DetachFromOriginalLayout) +{ + // using OASIS means we create a lot of references to array + // and shape repo - we check here whether these references get + // translated or resolved in the hierarchy builder. + std::auto_ptr ly (new db::Layout (false)); + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l5.oas.gz"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (*ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target); + + for (db::Layout::layer_iterator li = ly->begin_layers (); li != ly->end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly->begin_top_down (); + db::RecursiveShapeIterator iter (*ly, ly->cell (top_cell_index), li1); + + iter.push (&builder); + + } + + // make sure there is no connection to original layout + ly.reset (0); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au_l5.gds"); +} + diff --git a/testdata/algo/hierarchy_builder_au_l5.gds b/testdata/algo/hierarchy_builder_au_l5.gds new file mode 100644 index 0000000000000000000000000000000000000000..ecabac16a26d037eda78e9b70ed50d87f6ec4c3a GIT binary patch literal 3566 zcma)8QD_ub6umn;`(}0%qtzf-Lb1}83aOP^l_E;q#@3LMNOlcv2qd5V5el`Xh!sHv zs|W!Lg8Css?T3G2LC~)vrOlV0LQ4tsuOI~Sl|^?vcka78?@h9^S=c?xz4yHP-o59( z88Q?EU(!%1i2kDzm1#Q}de8k&abUg}s}l{DD@Tt0ba4ESUza}_ee~g-TQf}>s&wbo zjA>3CooEn+F_GC6P(WrNAfn+d0=9EvjAf zSEYVsdw%maS25mP#&3%Tw=hzYyj)@=D({qCN3@~-)I0q#Zhc=p!TBztKToi)vpCy3 zifU){>f`XSjek-T$SE2>#ozuqAOS}YcE90E2xh_tya($*5nICwKICPa`@Qcw$u+~9%{8oQSHTi zu5tNe><}2BS{E(sA zqgeR;9W8GUaO0%)W#z(ecA(D^)k6`uTUZfw9M|c3T~_O&i8}Sx#G06OG~r}XP1yNV z{X6qn`kHUMbS8=Z#CGSwv4|7%tn$fIte*BNtgEF8dpV4*%wooeiK?few4W8#&RD=- zVui>Zp~_HHdrl$fM|NAcE@S;8(&5tY(qTchGkW=ReC&6R@cSPKUs3JVJs!W3<;R5V z!v4<)hSNaYmWpZzx(avrQ1)HO?xtXbCr|4f>TXf(=BCGwviz973pn3n!Q?me@JIZ& zsP^{bB0f0d%*Wo3F+W4K(|9qzdSCdpUxcry_Uip&e%~SC_wN_JqT2fo3ExgA^rsWr z(P_NHOrC6_^!!xrJ)=`&6Q57w`64rFCl(Wr{PIcHS?jHxNZnn$gLjI&Z!H&jcaIc# z_pNzSxO+alRKz{Lx5ztnzQ{ZKEc4nYTWdz!J!{p?NO>T$%4K>ff7 zxn^)bE2`a$cTL3FR1n+{@l01XJp>|sSKHK{i)up`f>hD{d3ERkBQaa_|&R@y*TcoJjXtR z>Ul=GMIjKYO*a}nR>Sj$<3>kq+y-Y3(U_`utGqX@H*uaXH*cOVH*KCTH*20RH))

h0!=o6(FjJD*6kG@$t8%o8Bk zr>Fb#xI#{je_D?x_fm>G7uN@ro~}ZRjclihjyv&AaA>#E;JzP|W;vSsrO1=v>%lS5 ze~T>!Vri`ZUZvd` z7eln%OFuIstalnh{m>2vy8y21A4Rr!%MEYW(qd1ttBUIkxN_r+?)S)3VdlM$4yO3k z4r0ZrCkeYV%l~Y&2k|GkwBc94MENq`(?nS^$FgmAbEiI+!5?RaOi}h=^O!VyCAmH% zv(=grOpaK1n2#|f)tyABR&rX85UF#?XKI5}nWYC`z0sIGbTQIv`5J7z^P+^DuNMY;Qv68}&S28P z)k2HGf)#SV$)3VtJF|j!@vb!nQhSqb=b~8b8G9 zrYJ|w^jf%$81Id^WJ@q$)(aMLS6=Wg$!BGRan`~dk@_1<9r$ZhQK+6g58ToFd9JH} zbjHxpd@%l`_!hD*Tq?5-kK*(AJkwdtaipXtOMQcI4uD8dk}k>!iS`vYKDJ`9JIYD! zfd8$#YZb#BPRSAR6LOTHwJas3jxz-AjwWB)mhMjB9(g|8DbfEL-(j9e^_dE}T8O|* zL2l|@)o@@C$Vf+|H6@%D&1y0++5Re-2Rgll=8UM|TUjUDD-WhCeh+Wq?s&%=l+@$Ut=jbW82SZF|

k+|&qQ@7GH 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⪳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\R`Egl#$1y5om!1>#NcdeA$pQ-k0D(TYo33 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 Date: Thu, 7 Feb 2019 21:08:52 +0100 Subject: [PATCH 241/335] Added golden data for deep region regression test. --- testdata/algo/deep_region_au100.gds | Bin 0 -> 9603418 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testdata/algo/deep_region_au100.gds diff --git a/testdata/algo/deep_region_au100.gds b/testdata/algo/deep_region_au100.gds new file mode 100644 index 0000000000000000000000000000000000000000..b88c5140e3455bd8675c52bb9909ac625f324c50 GIT binary patch literal 9603418 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.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 uR65r0=> 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 z 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 z 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 z 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 ~z>N%F>p`LfKUh)2osrMOJ|9GFl)O#E* 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%0DMBS3gwZpDs6) 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 k6+E?q_ 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⋙Jnx}>5++NxbpLC>|{?Zlx zzJU3|s;{-L%YOTB#ZlWo1&RByeFIA0e{1~%)6;p-lb3clW+O| zvZne!`C#e4seVsBQ2KAmZ!Qn7RXiaNHUBIRl}9ZPU`TL6G8~%>GBfm`D zsCW)kPmtZA z{1$ny{v}(BK4hLplO;ACsETA$dFU4t+mQoPOKhvcG^@H@qJTwXRqmvVTy2 zSby9vtxL%Hf3v36EtiM-ZrSwwoWEaY{y&F`Jp74!i#*i+%K9Pu8tM<(|6IS#>!+#y z+TS61zpr&H)Ot4k{sUgm%pX5l z4*&bS?0u8;px0mRpF--1yib36-H|eWBI7>2VZPG)##Y{^KmLg3i{`PZ|EoRz=&b+0 zwwZFVPk-V9^CbL<(~Zl;F8w+BcdN!n z`PtMyc;chCd%tX=f3xy(NWF*T&+uoDH50cT^8dp&b8N@-X-$?#KSv^eY9%|ey4>i7)hq7;SgW@Mt zJWbBXf1%>Y@-TLZUG|foC$Hb<_%s#YlkeW;_%^AhmG|nucTLfU^y~fVe~5l7@6~_B zwI1(DsBxWqwZTo6ck@x9O zZr$VjZmRvsZCl;Gsk|R~ufF2t;;~CS?mqu$JS$T!cIlHJCRd&)=O@v6Ke%^5&QB8u z%lQdv-I+ulVjtx#eQuJxl5>;fK0iUuO~}LhXKVXi_7Pv6Ka7Lsk;g~#EA;p zlKdgL^9LkvAP?2AwBKbP{^dT|`!C4;>+|adRNO}1r$6z^{nj_xf4ydR*?&RlN8YQy zda?W+)Vek?b#y@VTX~m0=j(~{*Z6xU=1X_Gy^F#V`)v>3Kfx}(i;}>^iT$m-w|&{o_xfz7z9(z@Tc*74dw!X(ZeR1#?P|YRN&A#9;Xl^fi+*9j>mJ`p zW__$ox!7m_#$PXd7tD zN_j}!x9xY?H`m(7_rE-k@UPmle?`9gIPsC=1ODUgVrTdn*^NB>)XpLg+j^pb7}W8LLDzY{MzJmAD$uhV=_jrE%EtXbZY@A6V!iM`Ud-4HvGhX>XadDyl~`yKZA z`8WG{@~pZ496$fNDAae&$M4+Z@0*)9HANoYI9~L^Pt6p0_{ptBKfGhQ$iq*cS@c8Y zzwrl-IsQz=^Z0}N9na?RV?K{u6r%U_o12S5+SPgGq7b`~hx7}1_>R4${_w4OUW+_@ z-C~i4ng^DLTDNRh7(a>~{`UP1`&0aFs(`X#yh2kHNd zj#bunyXRkx2fl|%Jb2wuJXEG!?DGG5^4sb~f8Wr2@KW!`W<&B*EAP{1zexTv)o=Eb z^xsT*Pky6)@`m-H+GT!JqTkB<+MoE(dyD;0?M|G%Z9wTq-lgyP@Rs3{UmsL8WwzMS9onJ%V!%;l>eH^IqAdbQ<4_#5}3+0c=*USH*{FL^( z>~Xy5obGnv6UY7cmDthBd-bn*wCF?ZW&a(b-^#o6x8o1pm)URP>^j{ae^UJnsprmh zr9Mb~$XiCQ&)@Wqcy`_qz3zkQ!u}`1pd&~z&9^v^}nDV~)GAIA@ zJ_PHNseSv@Yu5VvrRHbWmGV&g0GEeakF3{{apQf9u-2>k`|O{3NbTwT2Cet4#mbZ~ zb-v?02>TD>H@y#0iT_%8Z~M>C`p(}$^eYQPDsCIepiT2H3Sg7@P zwSNWi^RuUIzo~No?|WPn>YU*6Q0EBCL-~vMMevuYex|;z_L%R)8~!%6?mFJWlrQ0L z<{9(B^G)-`^Q>jc`{oPnPk(5S&p#&PbeqO6R6ir{vY+*Z_xV^KO|2_aAJKZ=lDs+f zLB(&V{ED1>ZEAg)`nck>CF_gp3AL_FeSOyJi|HFYYhxL9H*;SBV|1yjOqm zaYY}>PTn`HM8B1H>2JrMnm4QukadOmQ%QZAAFdCoKFeE1uWx;sqyA^ic)tPJXH5U( z=>gAPQ|b@TJLUIkc;A-yYuxUCxp_d^rMxBknAG3ZKl^uKZ;4k(oZhf)K;pHP_v-7t zD91ZQU++&=qTkB9^x6OOz9;8gll-Ldaejh2ALEb8{GHfUAN}F|Cg(dy-0S_PO4$>6 zufF_eeTe=G_78}DEAP@@p#4`5m;ONP*ZW45Hy`0G~> z7Jotf)3)DbpZ#;Y{CVAte$UeVyZcz_A56K}r_cM1v}2MN_5P#tBTV^H<8$I#=NI17 zWZ%nsn<06Ja@f~C?@f`9Ap36Kp9)hBd-b&sv7aF0tNlnN{%hr3`W|2Am-c&(1Kk(! z{xs)PQ}+pumr(Z&E^o;_1oBGlOOSJJHnnfDJk)vEeTry(3M@FV-sQ2RRGQ^S7qL*;$7+3?N4$)_d(rkn{5E>i(MtLed6&J+Z{$3mhCDwWeQ$YwY`$Q(>*M`I>gBny z%fpdvrMxB2jgeRC`7wGtKQ{H;*z&M#cd6&&WB2Uw^IP+GA8E$^R?pi*J->~-tAF@s zeB&DHn{S(I#%E9Y{Unp;&#k;y|I!0RAELiq_JruS@-BV+JN|k-r`G#h=97*z<8Pkz z`(@^1?}wOaSM0KHJLCUHdz*_tw@~=+ z*EORb-qu_ker>TC{nMtoI6S$w$iwJ~UG=lS9{YK{7xD3-Lg|e?e`j-XsCq4L$@{@$ zzp>Wu2b)v+eCWQ#=8|yrh9VC~&S}Q}>vVHT%i50G>#~P<7<-G}ulVb$3TapE(LXcg zVz2(Y?=JR3^bT(rkbbrDE`9t>yy9>3J9{0^yiZMByT1ER4@iBKhtcctH~ky^t9ifA z0(l>9^b7KLOWr4myvsiO=Y|uG*Dtn*U^%%Ao<`u`vxRmwenv3H9v?G&lBbKu{WswP`RApJrfzVM1>?9=i?%cq@sTxmDNZr0NZ z+|gVdo}=eY$V2_^zR54X ziacb$Mf)N7ytS_*UK-A4<2%=td=A;CJYM#OvSa+kr%Qfq#fkp)3z3Q$haX78Mn6nuJ-Zo=vVdk%1hAuh6hhp zro1nIWB>3!$!_(}RJ+6fu*dD2vdiV6>~?vm{#hQX|HJQ=cUvyQGhk7sE`r%7<6nXgKD~f(7yGOpK@edVmqpNm1 z-b}^Y=<;hFZ>HkR<)Pxt<)Qpyc__bG9;)5ZA^qMsl--tx>X+rA{9<{iICcG@{2YIF z#f!)LQOy&@%dp70G%~b*K;`X`@!gIeQ+eI;P z9rt`Q>DOiVdHhZKjXcCJ$V2>wJjAcmAL4i9A>)EPWZaO4j4$$#@!x)=j6YOdjP5uv zAnoo_e1?jv(cRPjeS)dDvpmFZ^h5fEeyF&%UZ}XYJj6ffhsqDOFVy(k{_gqa_kl(? zZ7cag4Lp zeWc}~^4suVD89p=Iou5YW#jw?+2kn62zm@m3Kl0P3{5%%&-onUpk5uv;Hu7HmudOTkBF{NS9y(oV z{Z`(q|MsgI?sG?=?s-QJoER|WVwe7Q=I_xpeqX}W`+XyyQ9nZJ`@p`CD>R&jUwp+2!YfX3Ce+f6eKl5B2LuTA`fLh^<%$@Usb>T3-P=5ftAXa?8nHPCVJY> zSsyANv!AQne4xlf+C@LaF7(1Pb`||l?@d@9(l7K|;%DruWc=`t*cG3|7kQEA%_j3h z`H|=4=J8|A$lvX0_&tRAj=iNk)O!Ff4_~)f>B;8wYJZcUF%MXE`9tz{8yT9iXT(+ zll6xAX=+}&Jk-2&d8m15c}wP{*Q-$T3pw-4)co?g)3UbP6TfrVr~N7M4w;wiUn@2L z*xxe$OwBvXL+n7_l01UEvaPo#e>I$kyzkO;Ta$gkPc~(L0NFpVFAKkacfhJ46>+$Rz zGhUbNcl?e2Vamlmea<7y7gPHT&I9Z>%#@40`s}m#JE(Qu`>inLOX!dM;-0eKfNFQ- zmwot`_YZjA5;CuNKOszc-}{)>f3x<3 z8eiTku-@A>PShi&e5vsr`}wV&pOAUUeRIfrGm&@Me@OQ8T!Q|Y>Nn3PSP#rE-s|TZ zTUyR;Khw`2D&=>cKj42;rGac)me@OvN3~HHbgRK9KTC`N8dmv1bHul zJsu*@;C9$^D^>@sk}fOkQdFAi(UE)(jR}<(UK2DxBK2*1Cj^cqj(F+1ISz2 zKFKS|1Cl!*K=J_ckUT(ns5ngfUG{CqzR}@k_RqE#YF*_03D!qb>(=;>?DhI(YJcJK zFnVHF{q&!4WK2-EghedSITe5@t>*v1$yi+Ox>TjJlwDL z+V;Erx1RC&!)?wN5c|eIu%?nc5qY1!=c)D^rpD9rHB9*u`w`v`;J(R3PtQ5o&zLC} zyV{?_e%3+VPno*^8h^?L-+!6%%lKK>**_+6x2Y-d2i4B_=0^rpJC=v3SL|w^@g0B1 zqQ}>i-vxWqZsxoP~h zswZTgQr?n%-uSDPPebO*>vxy=0+~nBuVlVa-jY1)`a|Zaom-f5t^LUt!pHnu4Detkz`pWm#hv+GO*Z0k~>zoQN z*ZK6OqXVW~?9yjE$*cC0$T*PKLcY^7uJyR`3+pWBdoKLlCXKV@q2j>hA>-86-_^eR zuleNm;ls-BnxB>ExAH!Hp65Gm;FopJ{dD;+l>Z{{(qAB+e{Hwhhs4q7Zx0WsIO6`5 zc!G=%@=ATT#^s^>WqBw+SsuRYM3IN;U)t}o?~v?cUxj~6>G6Ih-{&#+>AZ9QX@8#> z@?B!q*Gk#V`iuRh`e%8l@gDoflOAtKzu31`Y8+i2%AamGj9p@%{UaZ^&i0$Kd*s7& zw%?TfJXhuW$`HGGK2s^Xd2WOKrtG#nl>H;OH?|*QH_u-yWw*;i+3j{i*&X{j?DzX$ z!(ZKL`%T%;`(*w;G}L#0hQFrq2yfQ&P0GX1+)(79zE5O%sJL*wq2k8!P;q5>sQy{r z62Bm?lwWwijW~vU4{4;)yb6_fM~1KU_mE8GWy?eCL@!i7EDtr$try}~>JRY)`l0+h z{Ec;~B+kwmig6^g{Z9ekea%FO;7x5Ag^3 zA@g!t`6-MayZ!C|55@1a_?{8u`$znqs`8RkUQhV`5$g)y4M$%2(6Le;ensmK@=)JL zvtD@5e36H3yGwmfiRUK717uy|`A5jQ#Pg9#=8@#APp154c__cRJZ$Tw{*L+Ius-s> zB63K(yx$no?&#m{{(|iHK26^dH185$s(Qjt&zJpL_$KY! zkcU_AF7oh|+OH!IUvjd@!{@In@-Xd+ef?v<%lCTC>kpQD+%V;0uRiOpzL)6r_txWH zKl#2Q>u4>PyicEV!seIW>+=HNX=Hz2nR2mDpL;XDe`dbyX5V*h2~#fi>g!x+efWy~ zcS1`<*7^ru{PC?=%@#j#{Z$iaeLtx0Tywst zRD4F>tAFW%q7Tv6^Q=nrTX~m0=S_akN_?8cw|>v+IERYU(Zyo}=I?s^O+MxKY|aai z`TiSg2PBWQ@-BPO=l8|-2h=$5yW>jw)5?4Gb)K|7L|^C4O7vTKmwwLQ#0&YHxZw9U zm5LwE-CM%^-4f%=eF5{^ls|kQ5T;!0vIl>7UF7drAJJod3{$>@KKF7S4@lnD{ahvE z(aQVU=ib2ngv8g|hX=%;t-Md4-}kwFsJy}N04r&~m3QfT{whBcpX6!ca_p8h11fKE z&qw|;l@~1!l{Z};Dz91|%5Rp3ig(Myw7XQi>)z1)gY-}Lhn4iNm3Q?o@s2)m?L4b^ zuS~hvrN5o{P<(B9+Mz+xp0fm*L;ta zb9OD4yicEdYsU{Hesup_N&K|(UVW{1)`#e8J*Y&#m3QgSwex4o?2+P!t@F+3-^=b$ zd3*G4u4%S}%ImD>Yf?h^6qdm-tYieKLcg((-i`bT_lZeV;&`zVndg-a+Ux@#ahZ=w0&u9Ei<@eDytG!U`w9CV*FZFp~OPF?g_PbYL zkJ@#eAfhpeAEe|Y_b_^++6tN(}8-thl=ocAZD{51Sd^(T~_!*Abj z|Cs8h<)P-G-@gmx_u=<0+W)53FU!OEyPM(P+TCmk&(M7a?T6a83?F#3*%E49i+x@8 zGybDGH*I;w4TX#o;}fP_?9yj`@Lo3Stx4QGWp9ZeD7$%IyOMFHJY<}ahs;~#A@dq} zsD4=Pn;I|PKO~;br!++#o+CO6{u+7tR{P78pIjcwPc9GTC(A?m$?}kPcI*Go zh1f~`A^kue;t%AZ{OtNe`PqK#_P6)L_?Pw2R34%})>AX(J?mlO(dSLh^W-tbV-^#o6=dk}hk2W*9?=p2CHgd4shrvxhsQ8k8gB(Rmwx%Z;m{y`>as+n*5%O`%P2!jQr~{+hsB?$XjyniGC$niBpZApcJBU8-D~Bmx%6`W= z`igt{&H5f%pZOqpZ~OW^f&We|M| zhcnCHg~So#AEq9$&)$(OyGp#mGw$~9g)52UR^Fw5i214C!_Ew89|y;0o6)O3G2p27 zi+WBv6TWf5@iY^@LHi5hEmVGDeIY-Yir>+;wMb!?cdx{@;9Wu8y5$pKFV804|dhRp!#|K zg?~-?m*;u-*(A>*56Q#G!LyfcbLXETKA^t!=)Oe5WKkD%|HO{toiSg!nGVx*3kH^aIVamm>{BcO*J^u4& zdb~`H*Z40EdAv;JW!{$|Kby+S+{coiP5F^~Tk^B1c3mDSFZ2E#`PtMs@IDUt*_0n# z9?DL)8>-*2ugiYomHR&85wf0g9~h>*Ctm$LmiuYsruJXF|Hb~x)c(xnE!l@tUa5UJ za`L*V{ea)MYFX=5{ayBxzehi&_>$bzK92jvnegwnmhw>Rw&kJhv|cDXEpO@P?5e+# z=ka$OFVEw}+HQ}1_V15wDDefEXS&a>B)(dCSO2W9=b_{qo`;f$ct0>q`BM4F^`ozL zJg#a#v_9=GvESl7SJqERKI47YFy(#g>m2ig_#h4;ar^;|S15msfAp07VJa_K9%|mZ zJS5K14^!_F^PA^M^bc~*;CWM+@+JF!a98oa=zbrTAMt~T9{2o9>R)!Gj6d9YWyAA? zO2)sHca6W}d-0h4$8#;#0iI7+%1~mgY{wvSfudm-dpw4lAf3PLzG~VB>Bu|jfLd{FdL(N;uL(OZ;!?s=KVW0o_9R+fT zePegc4=B6%eFgIyVi)pC+2!(3c3B?EF3UsNWqH`P+h?EW_gi+j|2$`OT>Zw{O8i1O z?CKx$%lV3NBJXKlk)JAwgU=rD{4$9X!r4e3y%RV^jB6e4mT^ zD>LO{hyBa8qyPOAzOOWO@5cY(=RVie{nOO4yL~@p>fX-fq3-ou9_rrD^6-KYZ7Fg5o&UVS_l>6RJ*UoYe4lLUUex6wcA_8B z5A;LbyIL>Qy`AMD{y;y}y=45?;a~4(r*A)P|C;Le^xenYe^dIAcjx`-OXX?6xUVlujOMGvL{jsTa%jKcgHJ69Vcb13B zbC!o%KP?Z_?o#>fqX+z6-6Yie@O*ErQt#=xe2Mq<_&%fe8KTdTpI=v*^1l7UoczQ5 zujps+^y}vjAPWcsi_!Y$i@=){N@=*E3@|K&1E^Veh zbVE})yx#In;rW*rd8qw2`i!HgeFOEge=xPbn7UQ*5z=3MKg#`u=>6sC0p-uBzdmMv zn&<8+@{o4X3$Y9RQ2Eb#q4J*PA^k(YCF6vBmGP_CJN^&vaDL%?@;oP=`1aZXm0w)G zM1Goj*=|4IF>gKIOucH>&xuSu--*1heZF_@{RL!S@mA%zko`p~@6+dd#HGcl!k8>W1Te8+cIofkyno$sxN zDTiJ5=D(@DkovmXyM*ykUMIge zk89pKzqjNbVe&g#pThRb?O7zZ=uVwXLP-^82N7XQOX^nKUsG#(-T zZ{@xE`xKx29Yk;c-T_m-)cD=Jq3A=ccN2#+jv@N3ytn;lX?*xQk@w>HzGs+n*rz}J z(jC@^=uQ8U@=Z(hBJa}oc9bBb9-;P!tOu3EV=M2~ zXMd*sKk@8!Oz|A1yl;PR`}G~b&DaU`-Zb9>t4w*1{R_+o#WC~E@vM0{apNwpujVDE zN_@BE+)jDrL&q%V9KbwqUO-NMfaC|tL(OxShi$t(^WFFLm zBp&!4H0K3V{^op$-%a_+<)QrK@=$)VJd~d-59KGzL-~vEOY=OyOuIejg+s*aZHH{1 zDZ3^$REkrPgchn^_OoKjRPf&tb}8SNqKW>AiP*{+r{o&Ga?5IDSpdd%l;*{5Li4 z`Q9nd15L#h-$&)X!&H2^JmlPe{kC%chl)$SXIQDYusl>8xZO~35c|69cRW1jq~|H$ zjrKjx3l9%ST($Bp{WqYJ-P6Je-B~m z9B+AewblpZq0aG^huZgB9zK6vk%zIX=l2kdhwlY-o`&Qp?)NG;Dt>i8$bFr8?ye$l z$v)ZlgQ51xz8?(Rdi&1nj;Hm?ujEyzIQIR2nDU-@vcB?#^`Y(=nD3S7xALy>v;G^E zpZPn;{jBbXDpTImKK^4qcs~i1Uzrb;>>Gb@ukTk)?Heo)wJy6nJY%uQL*?7l-(|ns zXPv=LsNbPap4vU&sUq5e;S<>6=d6nRU}XRTLZt+%KD%%_Qmc02w}#U10w?w9o$JU8+^?F7 zFJHKBrQ!aMs`@Ax7ep!!9tv}p<^8DJ=bJ~fg zjQe@5spq^d5A___<)NM@TOMlNbiJY0P0K?)x3)agb8E{(J-4-})Zz6p7y_EqecH-%cKEDyCWvpm%P&GN8qx9|LN2>aN- zD_@xR%$NOp_}LrEzQ5(BXWZxgd8P6k`*Y@#srkhIo%v+4zH8s_^&M{9Q0#3<9`pV` zWc^mYA^)1Ot0$j%K58B@KFlZYt2Cb~Q!aMQ2fq)<{V?~9rq&Jak6AxV&8vxzXkQf` zR=gk&wQsUKR9>_^Oub@P`}m*dG3*ab=Hcn}Wj;dvZjkl3Qok!?|IF_TP3FnNJN$bz z^Lo{TJY>H*a9Xyk|ePK>y_*pTqEz^3%l24iBjOWPi5wxlZy*&VBMX`N^bReyb4;9zE=i>7r)I3T3UF~0k{Tt>z&rHpO z>178!Urg-z?$KfoWdDdfRJ+q7d&~X=zTy+5zEJ1N>DitB{kN%koc6lx!JmFFLvaI% zSMGU3;yCg?eed5jAIwLtE&K5>mxr9|(GNM- zqaSjvM;?b~!f9tsIhxl#9GT9f(zUALNVEat9YkByL9Yr2qxWCB5^NtmHOU4!bO4*n8 zyX;$!eV;q(@qpO1{QvB$WSk=J(!WCb+@ld+5Wn%fyi#^^zjl5oyDbl8x8*HU5A0~q z=cS>AX}4#pzeB%u+Wj>j*1q!kq2|2sU&o8Q<@t}-dzg|}KH*Hu&ktn>^`0NfPU<~B zR6i^ai4)|Z#?$gp{v7(9WA>*>JMY`wxIK8*bTjn9*#Xbca{_xxjdGtf_%F~VY z&kKoHVX~O+mNqT{+CNUzNW@^H#Kf6e_8XdC4Qs4QvPzgq5Kv5 zy6k5@t@?{y_Ag{Ut@_-CO8g&rpFaLPr_ui{fxCCuf0Zd0m(t%jdf56Ke^I}amj5eL zE_UgYuU36ykNppA|G%#rQ2t%@t*!R2DZi~k9%^2=Jk-3gJk-3hyd`-Gd8PcD_B-tJ z{9k>}TH9wzfAzXy>zj)A)lYhyA$IdTOMlu zq0f9b8CTim@rCN&=&IcVYFzohUhIEO#fRmg@_@@jRR@TK{&8pZ&jLz4Y~Wru0^9DEg-AUGY@47pi@ix5Tf=D`h`& z{Aa3P)JwiJ)o+)F@{7wu`OWfBeziQ5|5wZ?enXAVirHC@k4d{fd7`urHLfduPH`D( z+$|5WAN`j26?vuN3Vq_tRQy>U;uq=LKTvUteUUuDY0uKtl9mwi&>q52_yU3SOX0o9LXcQ3j> zruu7nOU9k@N{u^m)&sMxw0$zDWMp{Db&!_{S9Aq5L=eQ)}!$Q+~6&C4Qs4 zQhq~@|ID`DzWL4g@&8`jK1A;y_6$hdn*e}+4d-4n8H~y-d z9Y3b>!}x0!oF7c>Tljys>|-GN8h%e*sd>-uwweE?=D+2k>>2;HG9OLl4gQ}l`2*5l z$}453+YJ?ev9D|V7GyvFSAh70tQ%u*kbR--;{Od`pQ&~&50%$k9?FlFhw``OEm?Pw zSGMi;%nzSeI1gX=CXE~Xb&ZqqUFC0I+6@27nazda@2oHK@MD)3dHARLe_7~FAV7ydg1S`E&AdA*iqymenCHsAH|M%D9_V%iNSbGK#do_L#otzKC$vqujl5! zju&}L_6d|%YQKR#`yEqpK)uXAQ*q+*P;un)P;q8?s5rDdRGjkrDB{(mowIh8_MrAP z6HneSp!PMEhuDjLOZGL$E0s6UCx4ilZOuhKql%HK5%FiwjLkypyk=;LowezrWsAJWI)ru^J|Q28sApRu=-YfhbKO(^E2ZLnIC_p`}k0Kp69OQdq|$={p(8N_O7kY zrzUZWJd`~X_sc&aaj)mM*Qu+A zEjQ(Ne%FQnP1QT~x{_Z_+2`_>*iCt*`i~s{oANvL;(t?qcX=qkyF8TNEf3{)%R~8} z-%a9wlXmu%@iyh}sr~Y2C_lSA#9s7U;%DTQ@-zDQ+mxRz5Alce@wX{|PhE4u{x;=r z?CtTlzvskyz3yFQT|ak9eDkrUa<$g~H=bH4m3vJ?{BvDvvLF>k;R3NFHDIdf5|d zo-BK{>@`O z_^V90xRn0-mtA80^|xMX{mPV!UHS`ZpWkO~__uY1-#hK+`5Rhp{E2NYul(6HmTwGq z9xw86*MTAr=XMr(c$M1G`veqHsr`p&u^}??986VzPqdil4yl;lSnR2mDpWhpyZ%S|Kg@>(gro2br1Jbi^m70ItBox$+^BW>o}s4l0=+kKm+n8pEvg53xJmEZ zp4{JD7@oc8|F3mn%Z;DY`Yn0oC+14~VeAsS?6?0`94+%(wExETRXRVm@-F=a@{{fZ z_&f7^r+waM{&MbTUQ=GF`RMXcdBO5fdBgHhdByUO_?{|u!rE?6{5c=Eb{Q*hMMX5xn^Vd?FWmz<%K`|ILj-4XAJgwYFyNVbzmM{e_R(*-5xXR>eDtLCH-_JuF7l9mN#Fg08VBsE zj9VYX2d-a)jZ~jD?f9Ns)!jy|0`sM$#eSOyV<5N)g z_tVx7?>g1E-tcdZ6nXgNJw+aVZEKN-x)-;8`0ZoO)WgHRA2;bI;}xpi>6QC@KW-k~ z&`eM4^8L7}dvTYC*o}Uuepw#=uI3y1p~lhj5dWYb;&1E=bx&^lyYI_=A2|JyEA4+% zexJT|yT`%&wVVCB;0wb?G{0}#+FTgQFT6*8VJQDt9?CD%$4Xq8@+-d&=J|t3KahvA z+wF$3JN9+NkMBp=AMwXG1@e%%K^_v9$iuYTc;DG^^m#5x$-1!;)e|)`voL*O1?!D*tWM+~KoQgS8FhJzQW-^(Sq5-Q! zt%nHdOFR?}QY6q)gNzt8*63R>K+%8$1}qSS(m`6AN`MIEm_kn-p;?(lvQ)*n-GlX_X7+U!UC;=NGB7wC0hPWF7V)&nu+UGazh@%*^gKhf*T^A7gN zx}*0sdHoUT*R^*brtL*KY^mSzZS~@gQa_a6@VmB#nzyU}@M6zjQ}f>PP;tTKq2i0>q5RU< z59PO(hw^L7L;1bsq2`6Ozd<_ej8HnN94bu;@j$vY5s?bYnF$!8~u=e zp&u%)SwEB=Ef28|`k~^2+Z8f?wS8Oc@A%+-rThfqpR51wT#xck?qA@a5Whs;P=4w1 z@E+yE$V2(1<>5zn6nR+J+cp2@YyV>;Cz8kTd$Ta*UG^ZJ zaL(iT3h~EZ+tnj}s`DS>xrrYm5Ak#4A@cxv$UH$FG9QtL_)}efXaBGh{s?Iw_6bui zw&@>J|JJ-@zx!vZ-Zj5?x$8Hz&hj1#)>~8SEbqT!y*1Tu-it;5P1Wo2Q1f)nE7Yz~ zcHw;%*vEY1t|AXLk6dr4{-=Fy?f3X9e&JV+Bl0uP&zdQRo%)YHQ1nIn#S?Zkw0a9=kl$Jhwd5{IopOytF*jerCXJx2BmHQ&~J-#XjV zJb!(Wht!XLP4;Q%H+*Qt`s~-x->Q9@~jpBE91PPVamlW zea6Y>dicvTHxxV6OgZe*=Xc>-^n990{oh(&>W3*8yY%TF?Ke+Px&K?jl=o>rack}; zwJyW=-RJn#Fy&%b{j}TXI-<{s)b23l{my%QUajk!Z@=8<&2wSO`_-@W+_@*~xdeE< z@&%puHcYwL)_(t9P4ScG6^Wl)?$A8D;z*C5kpEI1-ZfX`;b$txTb@58?l#xa_1fyg z-}qfC&nKFU(_8jb9xs@3u}h!#md-utbY;p;yl)hHnJE|B^cPr9887}19v8ne-s3If z_P@FCMJI|p{8_D+8sE9_S#w1mK3(}9^6-a_7I~O@JK9G)nEKo8)t0v^uYvDbUrqhZ z&K^H-wv>k&m#Ht@?(s1-ep8>G_V}4MC?7g=yxJ1xx?-Dsc2d9ct}Sm{D5U=TPL}#1 z^&$`NEcw-zFnVI!^%;NO13Xy5)WnHhKfUxx9O8V;~&f~Q}c`bn|Wq3 z51x9WnhW!P9rGdn;`YdI+z#bO4QUVM{oN8)2GZ|RSp@8Nj}`eUly zyvK|7o0{h?4;2So9x6^)9x9Gl9`3ucT64jJ)m*rLXOV|*-CyLP;+*wE#VKDul%09M z81^>#KYdSz_-JZ2TOTKJ?MwD1O4#o?L|L)*}ftV=?D6u?7L=e+4eVM z$A06zMfPS~A>;J}(hpPKG2ZjU7v;guXW-o%s?|TT*rVnh*JJ*fnwOS`nlIK5DcAmp z`D<$aU@d3#Z+A6Jc{^ero1DcA-+ui(zxS`sd>+PkN6#dskq?sP;tZM zq55TcsCHW(DlS}x7t<6MmRX{KE4vJdB@*vF*Zw{3U) zGgIDSAL0S;Nx?r%`33Jw!9UEDclZbTJa^&xp~i*x*)*j7THe)u&eJ`>Eb+w?QetqYciS{E!26-TF^y3_I0R2;TERGfBss5ozVD7*Ojq3klfeWUGT%0BoX z^T$N**~h(Jn6ek=%h=1*I^pt=deIMQ7y6;r3G0PgCoB)?ANpbJ*lzD8zIy&E&U)S} zzBa^PDevR2yl;{G%~YJ{{fgvCX3G1-Ki(T@eWF+Cy9--kb6h;)C;Qm~yepp1kkL>jlKm6n`4BUexkV{SO{0>m4MXeMIpl z#NTUqoBllhru*TL&Yh}!(3NvtKa_q4EeY0yB6Z_ zy06|4Kc_sz&yk1tIr0!cM;_wm$V0~axyQ?R!_?bnd|CHAK9G8~{xxKLYI$4#m@hmJ zVSUK>>N$yq=-2Wt{Z&7=&(Euwdd_UsRonf%nW^X1BJa}Ycht-W$a8A^o;pl<*LN%)Y|DgVbdlrj4)N@pphnnx! z5B0p5}q*U?25c$U2j+ZX8Zo^cK2ga#P23wG~JMXQQpg9#i#C zy+-+4`0%iwgP*Ip^<_K#JK2WnKflApzf9SM-_c?pQ|+00i^e~cU3eaz=k-jDqvbVe zr|S>XF0su&I6s~In|(g-GnHRW{=NDeDsE2xgW_zcxM_K)Jkat`d19{DRv-3WbxiHo z{RPNh{L<8VFtzQB*8@}SioB(M-!Gb&kRN_n?@fTZU*z}@ z>OPUn!+VyCJk)(6%R}8KvVN%hC=*kc_w%%lRo#0OpPP$V!X`QRqV3=^pM&lF8?!+7HZ=!c(spv!5e{xZN9m?*Oht!XL zP3(@mq3n)6_BUmB%R~A{ybNV`+K2s3+1>VOw}1Kl*+-RsDjtlB#F_EGUG7nFX#A6v zitnW8*Y+_5}l z{v!_+w=55}kF-2goFje`?@Z!};w$;3sl3qTq4ssw@3X&~dW`fhzqc~E?)U8}*M}(= zTjqz~&$sGpEAF4E{CL&UI_J;k+b^$H{e|LPc>+t z{p>3E>IbS-Uq07k%EeCohi)wTQ2S)!LPPXxd8hvLE}t8%g4&O+TDQK(Yo{xhi=FyU zIPLR1-uGqwO-FmwIiAbg&-1(3B?!UCnU}@?;Acr_Y41UkK?(iIBt1Oo-3ogq1Gkj ze(np_*M)8MFOYwYZ?pfI^1I0?^*@x~xIC2KSRTr6EDz~7`l0;B@=*RW`QOgke@u;! z0(q+Q5E+J!u%UC2Y)g*>EvA3IUn2UBlH`|f4@6z};z zQ|)3LX`d;-nR=c28)_c7JXE`_7pmQshibRwq1tVEsCHW(s{O1NoL8ChWBdVsHZ||8 zA8KB?JWM}2`b)f?eA1lPH&g4>-yW;$N3lWp8NokpRgW;q>EuAlv;^X-q;`EjVVM zv`cJjKlWes%jdjbgZ8`MIN6YXMc$@Q|0n)d?a=?3>i5K_cb9!PRKF)aKi!c2PyD@p zzY?+!{?s*PKMd7x%R}{l;!J6mss2wqu;Tth`b~L5_1pD_X;(-8-Tv2H?)7B?YF(Ll z?Ik_xJ$Msu(fShVJ$MtpyRG7Vc&65ciC6FS`etffaCs;{ocIH+U!m5OiFa!K3gu^( zhw?MmA8MU&yE@j3dB*QG2mO4edG79N)$0{U!cQr`e6!+7sOK^*58tMEf;@cVt|AX# zezLiK+xRck^|L-xKkMSEH_ASt>_dI5|EBtFd8l>Q<)P-O<)P-O<)OyM@-X#w*oX0* zx`EShea)1MZR3MJ=Vj#Yrt~;RbN&KT-jP2u zzW6okFJzsb_`UT#>O28|=X}%DIfCWk?&VS*>KwuHQ0EBNtI4{LykT8$zxAJdnDyV( zx4j1rKIztK5_$N8?1HUD@&81vA?&$W-TzeDQ%)b1X2Ux)Mit>MxaxI z(?aDrCkj*e`$Z${GFG7OkQ`|zkfC5SCj8JTKp4goO!=lLyfcL;oFplA`kIr z^g{hkCf9HC&ts~8{CAFczhr73!~1I4&zO3yhxf4ae2=N;dd6SB-_Q4$w~SZguU+u- zJ?8P#MIOHIzH0miotK5WpUwN+xvy<%A7y!{b4b@4YCo0swY8u4;Qf^T&s06+|I}xu zT>G;+V{aW5x|Ih97{eSKwTkopvJ<1L)7rXQ) zbl;QuO`V%^|B~}lGvyua$N$JrdH%qZ-N-AkpQ-*#Jb2vgF<+_m33;e}v&%#6n=KD> z-9GL2{YSOWRK47vq<%By9qnIW{^&ir^xxF@aesjEGv%MW510MEsdl%nevYMIX@!5wtqmz zL-~Bet973J&ZTmm4fR}!<)Q32@rP<}C_DQ9L(SufKRWFBZ0bDR@({m3Kg2JnCzPEn z4`pY|L)qE#Q2k7Q+U$itta{tH`)jJ*tB#iTn`-x}McE-#yIdZsU6zMxm*t__WqGJ} zSsto=slTm#I~k8B@9=#R^LLcT{P4PJE@V7*o+#r1u_N+O_f?|bcKvy-|BO3}eIa(= zy{|{+?T+O#ez6p`eSN6b!|NbrId&+kvpzN8oqU=$V2_^+wxGq8@GO_ zb=TJoKPCHe{ZM(D<)QL8%fqk9{%zapup@^hg}ixr*ZvBp%fN-*!Fb!_*&XzA5gR#Lf33Qy-`tzf8qF z%R|ivmxr1MmWR1+zx7Ra@ckdC=g+W5L!EnI59Dp_w>@<($$eGMC%rD}e6eB5#ZLXB zd&>F)S$B@l^~m~D%iHv^ANFPcZE}tyyZbx`e&Rqi@y{E3yyIw*hdNiZJXAleALhCp z`}bq2pYIVdpH0nYzC*-(HeY?C^DBOTPM*c@&R0Ecu1C$YRX@1L^Uc&evpgg|{Xfek zzCg`W?@vSdtL35Qf$I&^F0svi3)oL_*6|23&o4XFBXMBYVu=TkIDtGQj!+&FXOM@) zA><+P4tc0{rT(_|Vc*Fe$J{lzXdC=X><*B`3=X3u`i#+7_sOX2eZihXopL2fVt*JOW{*Q+o zuT1uRpI6)t6=z)@DqpcYRK8((nCpsd_GCQSFLG{S@;$-d+grXT2vaV0>3col+)Z)M z=Y5KEq0a$XZzONicmE$c>HYt>xb?Bys__TU_4q{Xmnjc*E@FA8a}mo!oo}ofR)1=8 zu7JGjej5Gf92)zZvOnj?tQTg=``J(XecK6QKke5WV!vA6)_>Rk%j&QG&-YDe_iw1b zVamlWeZC9B`-#mJ+2^a%)l8W3j`tH&Kj(p)ZctnmXI3eGe*fWyDTi(KFJM32FPynz zN1^Inb^f^PH%}k%xx!3%YN^P>JI@w*c-yuj5C3Fuk%uo`SL9*p72DcJ|9I|d=IZ6b z&t6vX{MAgDaX+rA`ek{jepw!- z-j4pwGk)ieRGXiwJQcp-{A#k=+vC$7swOw>sWyi{de-*^Hiy!)JiPe4q95kEVw-)A zG5%UlHb3!5;jG3>>&fPDX21P;{&??ZGXxx4tx*ziI z(EX|ZzpFi*N7G(tJAUj$!<2XR2Y=?dh|P>2WZd;!L_@~?_PNUA4-YB-L>@lpK#_-+ z9xd`P*A=_k&+{m>4|*Ja^UD|0AzpJYS%Epk~VZ>CT259S5&-&Fi&Ua&rzDHpry z=YG2F1+kOP;~LWbTHdC=llpbuJo|SSn_C7h@;P(p>nJ~J$n`$1@d|4_^xLk#pzHG; zRL5UP+|@ZyL*gRkq2j*fA@c%x$h<*6BrYQliOV`)a(sr0E2+P&eYU^O?>4>b0iVM; zztH(ym~z;rPyaYyWqcvy&UsA3?^52W@tCQ(>90>)-taHRE#DM=WLJ@gA3Rv(;ms$D zJgn>O>tDJb;C_fRW7_vWu+lK)9sO(WC)$rs=(`=Nm$=zb?V6-KRJ$z?)i29K_22UF z-fMloaZ|W5U*t7shO}?H?ZK*{+K)c{FlAS+k9|$%-=8g)`3E1ZeW2odL-cgN&-zgL zB=-Xw4lEXVNd4&7+(f&i*YI6i4~KPW$X z51kiEyW{i)i@!|Jg%nwuJ!*i$f$JBT@->ivWQQlDYbNyl3)yID1o$jYdzsWzt zly~)ip82i#$o|D79_x3Aj@R%Ninqu^<;j+Zw{0u(PyyliQE0#A@zKona&s3hn zc_8@?Y_6;8x7CmRS=aeLpC_q*K1ZsVa@eKM{+hfLlDDw`4pZKhe=jipx*x&*&Ll6; z^F+=M;OPTC-*0$osmMd^>p1_P3GqYpLi`kYsC}K~Vd@pT`p54eYzL8X_wOKTro5kh zxKHoAQS>BioC-=H`dB9!uV^zAo2qtAB?1q5RZ&jOe`W)4Lm{9CqomFD6ek4^Ml4 z%>Ipia#P;1Kc#-2r}y}X^qc4H!<2W~4}H!x?FS-$&-rGU@-F{Cf9jG&&lBi<9OujI z=S1dBa{Z8g zAP=zv@=$To*AHVyvBlp0UOWEcxF&kv{I|z?)cU}EcGd@zIH>#Vj)#!-?VjrzvhLd7 zLXCH>-&Vi<^)-rribsCFnK=1+#qF9Y@A9`}`1^}?JKmaHN9SwS6Y)FB8*<%W*jTO) zAHTQU$EoQ!tn!A$VV%!89z)_V@{l-8c}N^a9x5KEeVzT+Izv41y2E^BovE4fe({EL zaN-lRp3cL=l*6w2xj)PJL&lZ+w_(a*mp@#lMljKABh@efnp z?|X&hG339d@>ud2@>w(GVq5$1pNad29WPC-HxtSWS#Qj*+*!_vYtFuK%KO!ZTIZZU zhF6^_@=)VA@lffPsdbQif3_y$LU}{^f$I&m&ZT{A?Oz~Xe^l$8{tvPakN?$~9@Q>> zr^0${s$R>(XC5x)q1HvqL#>O}tBGBZH>~UJyPmvH`zghzamcv*Z|xVul#4C>^LjA; z)@@$*Or6&-&YahnPhDTRJk&Xj%R`;hSRU$}#`5rr4b}MDW~$BM&rcP3__~XVJkxVk$@%2NU_uzM&_n7?O2M+uGf_Zql8vn5Jr|{7BA`htt{g8H`AHJk2`r!-biaewr z=!ZJD<@s06hfU=TZf~Fbf%gdU|EAi>`-Et(nR2mZy!?C)=cd+&>ObeB4biXVUHX$x z-cW5;e@yk0`~924l#8AEI+w6Myjts}&L=4rb`uc746Ayh~QS-snJn{X-Fy-y@rMg%8o?Iu?ys}=Xd1ra3d1-m5d24y7d2M-^esuUN z<2Cu}OMD({Qt#ojK94o`pYZSEH;308^7}wGhuGr}ZY}nJ^yA1kKe&={;zW6)_ zGESN=4LMJ#0n$qWYALyGJ=g8aichdh~JXvji);kL6*RLI^+&@VFUbC%7 z`bT*fJ+bZj_#^jq=$}cue)V8!A56K}sjuhbyl;W>E1qv_sBz`FIpl5n3##Ae>sn`^ z_nBW?=~3$%=j+~YLal4OucM*XHOoV-YnF#v*DSB;eWvsq*7bI*Z{>N=$DA(t10-*F zoct$LUNiB%r~Ex@Q#|j`Fy&&K z{xRm?8l4|sv7?aru6)k%0A9W9{ITJwr6LbCpEwU?zMJeTbsoxjnt59B40-sOdFPKa z;Y&7@dc&8lt0o`uP&E^#U1D4N-GAM8_56fdr?~&xkoj55yY%_p0pANZo^PIiuE;~3Pp>*} zo6o0Bo#XSn9nNJS=lGO2)VYl74bv{Mt^f1Hr)Qk0IM0FHN8lW{A@%(1p;8~zeH_bc za^DSkL*17`&iyyDzHYzsBF>u_KU48_;?uhwU(J+v#8<|j=b$`K#MvLd&(A}JDTi(S zx4y>D|0fccd_Gw-<^A-zPsw?|?^|*o=$%Wx9{^L{bw7an_g?=LKYT8(xHS2am1;J` z54AqpAK_1F{@|aX;v~Os^L`8}j#?gGdac*X*_yiWi!`h#@-RE6q zt}Ax7|HXSf-+9k9>n!iT4w>hacg=VF%X)e~!=(MPKljf}+Rb%qy8XI-$a5Xh?`$7_ z!Sx{5x#`{>Q!aMu>wU`BhkEXY_boR>zm|9D^L}d1A0g*xyuUh3dDnR+^JDsoT}5By z+;aM*>l<3XmbcY!|M<`~-Vd#X+7Iy^fQI`|xc%&h=m+}*-XGpj`-Qcqbp91GACQOI z5Ba)cTm5eTo##sXMfdyE$%bx!E$`Gnt@widM9!V3S7e8pDTiJ9%q!xD=b7RP^NW20 zOnFCq@p;qQzf(LQzCh*id0KYOsm2N*kwZR52-Ja~rui~obzoA_MQc2apm>?FDE1+f$I5Ia#GVkhJw zb|T)?Z0hZ3-!bOn4fnczrrI_A?mOK+Q|H2b$7FL&&V{FMI#D%Lz0=p9as8(1wLDb( zoc_>m$4^u3<~t~~A7W?98_K?}H&naRzBcYY8xkjoBO!4@*LS>t(d&p8i+o=TP@8e7!LBh;9AFUVO)d_1E9GA@0l_D)C25dDr^v{*X`TJPSYDeDQe|-ly~e8 z=4B7Qd%*tKRQuPQ(R>fpZkLCL^nEDgq55NasQo41Phfv(=DHpG>jlQ+b$g24MeZw2 zzhzyIkKbEOze($CP43$vZ>T&5Ir)+)zp^}(AGkc!eLKrT{GhJC)4o4{c^OYAJMbNz zhKzSDZ_~%VQ-7uTul3B7-%fp8einXZv6R>3ek<~Zk2-ES>n!@d&wkIwhRt>Rte1Qb z4*xV6ms{kQVamm}_A_7j?xyvj{EF|7HblRccj~|4aM6d<`<6XDqF>9~^p8>hv(MQ- zOpV)`=biNZSM!6?*Y_)DYi`+~@1Q;R?y8~uX3cXRu>Y9yFUv#aUu#~fcoWLb{?27h z;ui9Tva{$p6OUZ4H$ z>d&5ZegrvpSp9_~4au7#Z)+d^!FNjif1>rjwZ37>VVged5#I|$4zbgk@2`4Pzg!-w zefHl_c?jQwoeh;|Ssv=Y!4zP3mR7hAHnjFSNeC7w&#Q^j>wk zNBURG+w8%3+g~+arpDQR7IHnsD_<9KJ^V3@Ui*0ae!%L5CC_J5@rdu#@;r#Cc;WI; z>yXPs<{|o_=7;6s7w@cAzv0$uHvH1DBCok+`fke`Dt|$re92V2kfOq zn#}tn2TB}(=p9|xqvrGK_iKKKn#Y!h)Q?`xS=uH2hFZtapAEInSsv0a>AQcBxTf|s zR6MkO#J2JEe7SMX`4``JWj$Gatm;vDlFR$#ORM!gZ1Oh~{XaWY^kK?7^e@K0wBB$Y zYqB2xlICxy`19&)gQilPrWAVqx{|LBusfnd*;<1uMaveG<6QSdXvT@ z)IN0eW9GfTGPS>2eWAuD)O_H(ypDfR^T6^j*A=_kzxt=A{d*qh_2v0m=kPO;^PI># z^|ii{U(kM!r`ENGDHq%HFQ@;E=NHeI>?2oyaJlps(oe}7Dy~@`D(*4<#6SB%eI2gX zR^K}q|ADh*{2}8#qHzitFUmvZ@s@{|9xd`v=Qoyzx({#t5WiZrRQwC#AFGe{h<{OE zhkq?#pK5)HSCBZxdKeO?C=ZEK$V1{3@{l-%yr$!f#*KFTc z@XKEbz{ww2Y%Km&8 zmicYw|2phVdpw@3$MOr-bH*$5`iZ}Etmp2}ma5DLNPm7{xku(hEpO|O^)(M|4~V|z zVMFXu%iHwlY5#xUQ}%-pe|y|qkB6r#zE{n8xOr%Mk%up-N_qIgxgrnQ57yUjtKZ|X zxTA~*WE{w^LdM|>8lRBysQYNWhZb+P`Jk)vB>R&wQ^D6Ty>HXR+pQo8R@3MYK+(aG{SGj&j{6!uT zpOJ^GKfkf9tUn^>V8|QlJPv)%3(dM8UGdrDt$5Grw$1UVA^NqvQ(xm@eMtLdkA~>i@-BVi4*I6_i9hI@DHl8S zpMB2HH;}*6Z}Rtsdj7%XeVzwmKkxb>^=iN0kos$RSN+UKo+mZwzvij?4^!Upd>rFP zUdHnortD9ChW*WyceH;2eeI*%ez>CcZd>f}^a1aKcwU43(9Ef&A`kC8TgpS~ML+zL zy+t0rbX}2$*r%?)%|5h`_1FFZ)xOo=eYi)pi*=XwnQE8iq1xs0Q0=ljRJ$w>)h^3J zwJ-Izw9osaHNURsE^atinKz$wUb`vO^B672BIo%fv%YS>-vhIb;xArLHJ`|5nU65#{rrh@9LHmb zz5e7xkJ#~+GxjGFJ0lMn2jn5+ggj&%k%#yL@(}yh^|#q~hIsthlcgV``}Kt*4c)(5 z-l?zk-2cbAuJQ4DUo++X>_h&C{ULE|^*i?TsCY-dNW6oLBl3nCN0*0+!fP zUx4+M@A&^j>nYy}Q{Go!>mtwJc^##G)}NXw?|MGZ_Iq$|c}^TEeoZ~Jtw-!v%iHX? zQ~l@u=*)tiOMr@VYacz=qvEd1L*=8~Hzgl6qbIgq|8lOsSUGPrKcV~MzcyaYgo+D1 z-$Z;cPc0RB_{meg-#rs5@67eu>cbvuzr5c50Eq`{zj3x9eh_(^{yh3)N2{6F?JmR~ zBexcNK%09ri~TfE1wB-U9qkHW7Pj6yFFh`%_E*eBY!jX zyzW{(f5G#*kmq#QUUE^7n$J9^#(Xvvk1P)r*VaDgq~n~adBXi~<_lyVQ{E7JQGclU zoc6V~ANzA(pZ|x%Q||AFDTgik&R?g0TlTv`_miR40qzes)c8*ynzuih*BmSIP`~4K zd8ps{T0hjd_`0FS&+<@yXn80AdIi#)9B?OHD#pB4_3_#iT` zcs{ow{d~u|(qE`N((;Hi2om4>M`YF zm;UhFB|gtHb-p(IlSh5NX6l@;kw2oUhR3UFboF30S&gn4tg7mm>@z&8{6XVj$}fh0 zKBU$Uvho?)0F>O9?D;ae?t8U zXT9H=>xkB`vyvS?v z-r-!orG7sjJ9g}JwPoL-%GCR$$ByfMPN?@$kG=1d-&bwE_JJY~^_^swhsPf(^6-wM z<+|Y~4itHK*Ibc@pQ(yG{QQX`55J^#-m3fGq25zHc0%{NL+U*_=kGn6`mXKRa#iuY zXY)Pli#(*==!f(R{qT2Jie7l_bdiVH2mLU16kF`=^=|A%i~bIQsrOos{lY%K-`afA z=_>Lzef!I;dcX3A4^$%}-=7@0eW^$J-N>DX?SJO^>x;Z5-&v!)p}xb0KHp+5p; zw)%+|qgUyDBI=(hKNv~TQc^)LhiaGQVe0L$ALCE^?SJs2JKW!f`aa~yZ|?T@AtB#|9GTbm0>Uk_&)z%z zeLqw2+VT*8eucgt5aQ3T()<2v@?A*e4HdVsH*wa)f9v+O*>?v2eU#phPP`W{eB49T z=wtUbOgZe-|Eam6FVfD@=T;4^U(4I{=c)e#r;9$+yc>Pbc#oQ&me;&+{D|cZr}tWZ zVMzTSK3eJ*U4MQ3w)*M+Sar_-2U#!2#!ogxKk_zx?$?ffVvolkqCfhN%ROp*NB>%O zsfnND|J$zT_MCUHv`56AW9RQ`==Rj|HvI+Kb3pz;J)+z5>e8=KNIMQrHze*+-Vpms z?s0(kEEjnwf3rN4zgs`7>+On{?%%W4mHvtN@z_tFY3Tmd^0xk2|N3o3U-Wq0G~Uqq zwY*Iq`;6VW!}fvJ*Lv7c_8B{+cGbi#tdk98ALQ7_lwB+jWfzx+vWw-R?34Oi+UNcE z*tawu{2xT`J1aeMUCKl1MIO>F2U9qkG++SkU!?ItKm6GtMpW)9{-v5|;7OUaUpQ*Nn+SjmZ!~0J7`(oy$7kmECh4}3aN2nc9@`tT<&EBayTg)RNtN&E0m<_FYw z6!EWywCk&jrG1e0BM+koTdt3Pj6AKx15@*M=nw6zD_7R67R65nR?poRn7;N_LTi9RGeTvZz#Pn%0pe( z@=$iNJXD;rJluC>HTKcVs=09g&LR)ry1&Rn^~?I9;-s%1DxR``=X-zV`|fjo!uO2G zQ?Sd}Kb+`M<1+RyhsypNzWHF0ht!K+NW0JvWq0d`8XwC;`h|X|@ppU0w(+)t0QevR%^yF#6txjdwu$V1x6{vuQy^nRdbuIu%u#U36{_QA53_ruDoyq^tI z-eFJc-+I>H`}TPcdbck*KQr~+JC}=X@N|jzhpAU=YoGmvb654>=dUOBR3rbn(cdREmlUUvhw{VG zt+Gd`c8)$p_6XHZ%fo-ZvYh9Jsi%*QggCiPNZm~yeLKaAJt)866so9I1P z_7By+$lLUH*8Y0oADyz_UHG^1clNI#*U@_5>p`xEJdB>$cKvy-kDl^#^O*ACw;rr6 z3^k6UufN^jhcq?Lqc`kw{%=zMO*5r_NWI9zT$lX6V?K<2`i#HdXv!ZsCnw)G-?*!a zyi0%VhYq{_rsl)gkMHq(Fja5lo%%X|y-@Rob5`~_oVzwmd7t^jxg7J!RJ+GsBYTFL zN1U%QpG^6s<)Qr2<)Q3rc_{l^9%>$09;V)o`Go(C{r$4%lPSL$`zQ4?l;20*X`g>9 z@oEgB_jUC*M8B4I>5o5hugAyKJRSd@ZJw{D#wYSl{qH;L_K!pKH!t<5`d!{We|?@X zeDhwPpPKC3-hI&fLsRGU!@sZd(2zWRLFdb%_JNj%x({HzQ1=Ne4|N~G`r!x87I~=q z5!Mg2UvRs^FCD6e-@nc0yQa=}hu^cy=ey=-sv-~XQa+44)c)3b;T=bdJfz)<$=WF<5L!GO! zzv6t&yl1({L!GO+Jk+_G^}}zybDZc}M-+ z|7E>5wcc@mnf1<0x!C4^r&v$kwZ5!(koA#s=up?OJY*d_s__X~2X7iL>mg(vL>{sZ zQXaAnA`e*yk%wt$&OU^N;?RY8UgB_L(Uc+w^y0AIA5w z+X|n#!Q;C%B>pI0aQuP9pI<2Z3p09R+x2PR$c*gGI6>KUpo{iAyqeSTu)qxbpOUIwuTu&f)(Lb$&6r zf49#sOwNb6UZ`^x>xVjru{_i{jpd=vaV!sY-eY-Ty; za{WQ)zozoG5uJ1Me1=K>b;JIWpF!n)E)SI#S{^EYv^-Q^>Fb8dJ1r0Ink({9d8_3i z&sWgCQ2Fo3P0BYz@`ETso=Kq&#{JB2op{CAnM_#|;b81uPk0WnB?(;`e=d%94Q1fu)mc3;Q4402cNjj^U>5i z9My9mb0PK4j+go&^>2BoN9v_KB#zeCZ>yj2;l8B*A8K5D9v^a@THdLD^0J~2X^+lJ z8lqpz+w`$7_o=Y2`8&$jbf1d*9j30w_;Ni{*K>Ioy*~CGd(?j0*OYz7)^D?YP1)7g z4P{^Q2*%f>-p%)w`XTiq4`tt6zpa1RcTCSuFh5QF_ovSk|Ai?RyYzkkMEfwvK8^b) zA@|E7Z_{VLH~iS+<^DBP-pO;c4V5qPJS+K@seH8g|Nk1leLsG1Qu(3w3y^)!;D6iR zqxLz2kDm5^$J9Q@@|ryNKzYM|K4dx1NAQ2SZr6TeLH!y0G37tn*O=Nb4*t(^?-xz& z6I~wOvs~mM^`IZp9`tLn&qLl&`@F$Rl~2}e>SbTmVjuFUfp4Di{u*x8x&6R@p6F3| zq07Txxu_cW_OkOeQ{y{0wCM3Qb)VGw;hx1J4|U(c@=$r8^+M%|mWRqy2geRMUo|I= z7kN$g|L8ZAf2BWd{l&ir-m=|(Y|6g}-Z^dmHs#lmcj*rvT(rI^{lP=~tZzy`@=pEF z?QlLf2+{vn&9_kNrpv`H{p!#5I^Q*w$5wx#_z)@&j=WR<$Zq>>CE_R5;>L#fG39;z z`5Rh~WamMVc|A0s@d-8VgAdCeLXErSp~l_vP~&cSsByQvCgX~{VO=lvclB@Z7uS_} zDS964Khx0jwU&44-+a92i=2xLzW-oD>(}x&{dxJ<(459cdA<3*`>LUB8lO=6i=pk~ z-d~t+K3L?T;=Rj5#cS(_xvtn&KmJX8ApbCx2NDm+2hEg=ZTiHkftSiJm6w?ZG@pNI z&iSsXJk{l)@>G|Hcg+=fs65s3PG{g8I6{)V)_mbcX(|KffRe!#kpe>Bv&l*_|U$zPC%I@hv1ynlO< zhhIBa)%0f_$TqdlfvY}3a+ zLqBuC{%y)Hhpy243+0zXFOdI+@@vaO%{$9O%}dL}JB}84DF3xQl>eswmiGDim7yO$ zYx|fwFCDsQm(Np8^=oKG@iKhd`XUeCI#XFM{P1N(9_qfQ^+Vks^>xELE-w0^>^t=I zVrNt1JoGHtJCq*`J#)$DrKbGE@=$){@=$(ec_=@$Jfz-BZ>=s2X*bso=@;@4`ydbH z@4kL0e~LZ3>`ng3dSdF_Xz;P;{JfBwPFV_!`tDR3g>3qcG|9(L0VW@a9_#;{mL#=z3htz|9NITFEuijqt zL+xuU59tT`q2kft<5wK7%-B(Ev3KS8to6frkjVN^+z(R@+w_kye?O=7fO!P9J`F8v z{Rp)_4V}_@6*6y;hgzS8Ry5y3=KYzf%zwzdM;>Z@8oFEYBGkI%dPC;*7pA;EnQ52U z)_(5?&g`(i4M5p%;J!UQ%I*UX%z1xlYTPUjHU0xvNo+VW6-Z+WQsVR@)=c0c-z_uz#GOFjkWQv zyrJ{iTHd8Uw5sd}Ao@d(*w_&L$lLViRsX<6M|?lXWF5YEZ&{C_;_SdBYFEg*PkE?y z!}3t;isj*N-QHZktA56h`%$L!1}`}7^~uzAT^_1lmxpSX<)PY-f3Uuoxvtn&KlUHG zP5Bmf5^cYwvkg-Y+w?jA9N50g=bxs|KL=JH^7*I9eEP24Wj;Zjd;0%E=85t>o=-KI zC-wE(>Z85YU1xnC20E|&jN&2rrO17m$lLUH(%!dhuePqADb#&C@+a=!ndF=AJznxn z$hiXYFnVI!_1(TB@<;MqNV~LuZJ6?o{C9!=eM|92`$x#WbztN~kDOZ!EPMZI?pZAI zQ2SPwhuXJVKjhp3{hI7skvFXC?b^S3ydNifsQm*_&v6bs`*e@A<9SE@Jf}%JkcWDX zb713&pXW4loeq0o&!L+%Kgmzv{mPHttN9nETC|_@gU)1t0{lUL3ehKk|!M{7)5PyxlQ~x^okDfyrgy_HhT#qRi+w|x0_ghZ- z{->$?o&&e-^?g|rKX}jW#UCJk^5@%nq`x1VDgA}?7kQ}ru(^I){fr;yP56sR`*j}W ze!!IX8~>Ne-jWYN=@0$Na*rwR&_Bj}Iy3M6fT_H;df+ne=ge2%=y`Tw_}~Lo_2nJa zh2huERn->`I6pTPS1k{f-&YTx@&3t_UoyY&PgC*S<)PxR>kXA(r+sbqWBiEk_7l^{hOSg3Jvy`jc$@VBq=_?fa_+Sk^8{Ez*j{RiSd zFWAu|{!`1_^cT=Sb=2_~`W*4}o*osC$!|GNH1{l4L&!tLJ(q`?UzUfOf0ozuIimC% zDjui)w)VMyii?aN@zMQLd<;|GHNNxeU-g8I&JRt+!Rpbw9T&~tT`BTVd7jHd<$=}< z6<@0h&N;rC%KvixmiCwD)rOQ8a9#?v9u1FeZ%Eu59@**mXHG9zzBPQ(twkRG;5&*u zlwG+l>zygPbA95Rsc~_6sBv?7sByJC)OtKTxzXcq%AbbUPTSrl_3FOR)==|q_=yJ{ zKTXX`%R}0Yen`L24>hl?7s_8P53vvWH9gL1U&GiHKBD^nzYoUtAK0e+U*lwIA4nd| ze$f2w;c8%m#xs#GNIy>>?UC!&@=ks2e?1-$eeHi6GETL;OTT)?wsM{zlK)r#YrG+O zf8<^I+(+>F0aX1%+qU({xk4>((dva^P~FS?InMJuQ}p))=+t8^|*_ipPFx-De_R~ zZY~dXE=N4%e9qK9&espMk8^pbb3e<&`?nW)NPa@QLandW|CsjrYLcHQ9<%0D zui_=oFA%qQ-h+71Fy&&W{xi-i`tWMyjaR6jA^NqvO@Alx<5eg8yoaghJO*BO$j^J2 zT;~t89)w&Ed3gD~jegtp=ehooi)=rWbFZ6L{Je+x%5&aF^E?gvYM%38|J;yz-+yDN zA5t&!P~(#8x7Ck*hu$>p|2Jjdp|`32P}hmPQ~zc?55)gLwP$GQY>z1y+w`$7=b1cD zW6Hjqck(=q$@RXuSgr@T9`Z1HV%znx@6b1n*}kUiOTOXf2chgb^mUDIDEpFkU|&=9 z53fGo^_%nqc_{nl`fc@NU)BfT4}yvltRD?Ek0Wo>=R9EO`jbBYF?IgKK8@dLn>znt zzs7mGsdFFyU#NLV-pcupsd<>|wbf^P$sc@v4Yj@vy>5Gt%0uvnEuq$R%R{aEmWQlY z@+YrbQ0uhi?d!GkrRv|;mGug;j&M#Bvd&N*-m_ffA?p_>^0H&NLVP@k7<%nGMxk zsP$^_zKgwXna58TdHBBjs=@!zd=E8m1|L>j2{k_~51*|2N7Nr`y-NGK+CT7rcG+Qd$-sd&2eA@5xg1pCz^Ph(DuYo_i#QtTNA67ja7@~EM2A1e1_ zq4F%tL+-;;9)7CCYwi#D|CGmB9=_&CDGy(MvZ=qbe?N1e^iSk@K+Y>d_pg?B>GNC- z`)SB|1<%!lDepSpwS8W3sMIeKH;A7NHU2}tEqm1@ZXs`|xQ3kgW-9Jk9%}qu9?JhL z4;jC@{;vM{97g`)dCK@Re{1@Fh30S9{R;fY`+MTcnEECAvVRWkH!APvKRoYgInRB{ zKEqEP?@@N~bDuS_3-X4t3v%pZ$}X0NvWv?@*~RjZcBlUTx?k~ra`<~rI=-1&&xap> z#Ot}K_}0if^ama{?e{yIpH)22c|GsnHTB-z$Xmw8>*L_8zBl#WZI$^U#XFr(@;y3p zN#~Nt!;|HFlJC)3e)oDhPx^d6(|rGo+cz7=u40S*{X8-IN51!MUaEZH(YyT}ck?+~Uy+B0wikJL zc)G~LcOUWojqkCsk87^0>$lZU{NVXkzPD#ekLO>}H{Y)P!GE5pHiv45%R{x-@=)JZ zv^;$3`XUc!HJ)5QOub@T`OIcR#Ezw%MQk^x(p7zaQQF(Ee)hxreJwHTmAi;0yQodn0huZI_q-5A_`Y z?i2I9HS>c9o9ngJx0Cv0XWq|me*Hn)dsC?Im@z;3o|*ZX!$lrOPi(tB_Vjx5Z}%2H zysxY`HTg~@@`l^SE$91@tXF(j((>@i2fYr>*4&)yif#4dPdtZ!ze4<&^R+PLUH)wQ z99%5p2Jwequ6o1|C=dVi65rqC`!(F>^my0$ZS7mYK6jn<`}iT>gQz~Mc81@z(a*Ka z)a1Jg$Q#P;)o051nHqP?Lyf=7!<$bQd3b~BtLyJ-ALojEF96c6fj6jMq1wfHBJG2; z3wcAe&*h=oWqGJ}SstogmWOq{UEhna{XU}nQu$B?m1lAvp`r3jmxszTEf1AvS{^FT zw7e#HBJzgH6S<#2z6hIoJMuC7k9o-Vl{_DR=2kzyv8iUt`+a|je1dptl83)@y5!?9 z<^AHRp8vDoLj3g4&+8F?uH|jxKTrF=vFv(GeOHP6fbT1rT8DUEkM+o8okAXd@>J0e zDSue=B~-g}{kHn=RsB3)&G?&|$J|e1zM6_l+;1X2LB2;qc|-Z>(8cTRucrLe{}<}J z6#Slr?^8g&*XZko@@Mq$XH$M`c_=@2c_=@&Jd__>9;P2+OMjgo4*mSP(qD+4&Qlw5 zUCKl1MIO>Fo7nedXNrBH z;w$I84bgkmzM>DY^Xuf#A>&1PNd4&748#2z)_jp6b33*8Wq|f&?P5eadvcEv= zkA2$h@BK6Joa>oqryR#Og((+X#?SgZ_vm#+q@O(h7^WO{>T7@E{17tVwLfWi#SZUV zkay`1zUBPVe#pB0{)0Wz{#xFp&-t6%DZ1S{9}H98ul?LFb6ga+Xdlh}wJ_zdt$yY^ zzXzayCVA^)&-wd0=GDv9;CFBI_nS=O8S;?)3;pmm$lav!1!rDXgi5MhtfW<;p6vuKS%z-zHXLtFv%M#Z*h63yvFiS zd5`6x@*~Sb{I0IQt$o;s-~Vv_1Zfw)7iy^S9(wj+kGHvIrO0d2PRbjqo#@Yo^>w*^ zTmASac@J{PIUe~>nDV~*ildHO#8do=cv>^%UGeOg>L)*8d`#@7{Ke;)@Pp_1_XXjW z4cd>%4;pIR2ETb{Id6orpXH(A;`xt-r ze=y~+P5&CkPw^Q!oZWEH@x9^9u2LTU)V%F88$Mg}`#DFe+3=Z~Uzf@r;icC)-p|%d zJzed=pRxDsqwX%GUal9WTx{zP{>blg8BbH^O#FV>=dw`ehWxI$;o<2b4|QI}?|(V3 zGUX5aE*O6>b?)c#@OnM3^WwwR=5S_zsW;U5CBF;i9Mep@I?mtRf1Rh;{*Zp_T%;l6 zQ_I`>zo7ne-)Hj|&zQ4UiKq8e4PT&mqx(U#p~lVfP~&EK__WhS9_rk|@|v5!dcyLC zb-mQz**-nbK>ot>44mr?{mHH#l~?fl1+SL%<_GiId5g$8#k>kr-fz9~{8c?B=VQc$@Zo(W zKGdXN$QyF5rh0w824xrOZz#K19=>qO@nN>+=C9mY>J8O@$CWVc65GaSUj64hVs`pm z;kuLlJ>hI9yAIxRW5xNPiQPZ4t>T=}B%bJe!|?mayi@;GC#ub|uk&K`UU#VIiz$aK`u_bo&pX>cA@ym0(2#Xd&ttQ`nAG!_jpe!` z=ctr79NKC9+3<<`i#(KF(8oTe>}UP(g3F73NPo8PD*b`XlZ*HED1Wj%WS$@oHBOd? z%oF4x_2~KP*^qW359tT;nzn=F4drKCpZR9W&tlIOdzbyb@;k=E`JMXb{HkWkJI1Tp ziNET6Z}yQ76yk?(m;FQh5q}QxL*!xf#J2V=aDClxV*djKw`P@aB_69@h2t+kf%<8SDdz8$7QPro793%r~C*;C_useyHb1 zoFBrJi*5FqM_>C_@*k6RMipDoQw7tl~=Nu^VQ1?}>ALhDZOZ`5tvwvy5 zGWCC+uc7W!dY*ZLrSUC2ZFg*=oUef>~&v^>;(Y|BIC0hWi#Lo5$t*FO0G{bk)Z zl@D+|@-8#wV$1lI^E%=+;{~xJaXL)7*rm_8sQp~D-|PG_OnG1bC*M+>BJZ-^dlm1P zM`Fsm@&)1p_b13VP33{yzuzuos-2E9bG?H_?4H-#-yk4m<0A{!+=OM9wkU&xFpW zYI&PJ_T@e{>kA}q6Hgi{uDd+cy2pKP@+njMNS?c5KWVCb_yUb@sCCihq4ts1tC@OX zTl=sN&vUV#G}R7YFHCvIeiD7o*SwDxeQvjLrD4ju_V@GB=X*KKA5(E{bl<*;=P69Z zHJ68qTP_bZ&nyoWr}(}J@!QOGJLVtf#iKuZ*!vli|Nr5mr9F^(f8s!o)Ju7&I5_%~ zd%XWO6%YOYA?>e^cuj@3&<=GvyzV zclB@deP@e5LB{0+r+Z}FC=VG|)NGb-sum^W4;V-thD1e4YoJ>vH{$=R^4}h0pUv&NKNw zMVNB4L!bBW(r)wNofY3>o()q@zdH2C_sGAMZ$j3|@x8knro7*Ji{BD2O~pz4jr`n9 zd7pU6`{-FWAnVB38@2w{OnKM-e1`d}_nfnzgS4OT1%@eyE&X@CIr49dUhhq<^UQbF zdsFABBVW4C=dI>7$4Ys4!`($5zWeqf50xkQx}ov}UpG{qV0n0IsmMd+36_V-6Gpzi z&-sF>b)WA`vi_T@m-sUi-gLYgU31d+^Gw}Wvpl5T=!fc;<>4z9i+*^u{EX{|*a!Vk zd57B-Dvxpd`{Xlx#{_*-{TP0a_FrMjVT=EA9?kcVSkEEn#`eQddBE7e$ge}?36_V- zBlxZo`GhII<+}y=uc`dRW-;kW^HUuXF3 z$9vTMAMC{aALuwFc|+n5lyD+Cx15a z*QKMyUm<>c`%;hiIprbq0C~tfK^`)XkcZ4u2_cHOBQ+_de;#T{I`4WxWC)A$sGxN@eWpIlDw!9|u8hJy_Yvg`j1@fGl zuG=sE@jXz-D=2?pel?UIMBe7#*nf0J_i4!c;a3*DKW>=vj{U$+^shWo&HkO{wYX(K z-|JQU41FEqE7yfwkN6tadg!;c5BrY(=0(nDOyx17uawTl{ zS5ZHtUaeCNl`rM`ZS`Xx;@UmyJ5<^)_?T*o)vP49^bPHQ{JV&fIfMZ<|iaC z8{b{_d*zT>A^ycGbeS#?$+x(01-V+(g`9uY^0xjkUx` z|Cg`#kMrxQ>b*}gllh%wP}Hc!1~SP^V&JAyAwaRIL>w_fxD*IBl@TkB5^R80A`Lp0 z@(}0%xzMKutdJ{3f{hq4LMjCU6v@?ciBc{B$~E?>GGM`YDLU5beLnBcKA-db!*jlW zyJ(|kl8 zPVKcn^1LE`$#aaZKU5s%dwx9cX!4w4+V`L5o1b4`A5FYA^&a!8_wM%l%%;{qzNg0e zXKGz^d8qfLtyh!xsF63+d8WU|7OMZ2hsqz(Cx0|$2Y>G^ls(vgvyPdv1N(FAVWRi3 zlSLm=Kk}Nq$Bn!p{ga&cwN0&mT$lCF)Vk;LQ0tz{Lye>5q3mpV7&~J3|7>sHzw~^S z9O4J8A0guqd8huDmdgDS$bFZ;J=G)kQ)+pezUR~Br@g=Q{^+9RYSm94@bd|#o=fn4 zsU~?QZ|K-YoFsE=gYFM^XgBY^m*kILi|wY zsrE;RAL=|cv>)lbFnq$F-&U>@Y8;0)ZLj$K0yFi9ZS5g`@tr^NX-K}l>a5~>&6IcK z+dluL9`@U&>@c)tz26TqWha-1vZKpG+1c_?<6(KI_hyD>w)*`UQ}5qcUX%B7C~r7? z&~na8_&=UQ;rb2b4?|BFu|JsV=g=h=++Pzt-M_Ow)P9BgVGXrUu{@-H^lS275b}n4 z9v^+)dojPPaX}u^Kk4&+kg0K?UD(@4|QLS^JMO? znYyoLc^Eyh?fUo!-^1g6s(EJukY^q{~&s=UG9xjRfS+l9P zPrSzd{y)*@9I{uKa@aXOItTVRh(6Ezo1+a=-Zeh9&wreE{1_H_j%;M?NROJoBSR-V ze<5)ic|*kwmxqcgmWPTvmWPT@mWP^G)Z2Ali2rjAgB%hEIEM*S-lczB`sBIDP2%Hs zc9i%C->dt6)pEnF-*-_p^zB_$L*myzUQ^;1Bz_?e6~BfDWw%i68y-31_CcPfL*9_M zLcL+yC3cN3`6T}jsh7MlOnJZYW&N_9xG#wQzwPLeb(s5KHN6f?-caL;-0LyaxT4=s z{j)rjeJu~wuhiewzu~)QOMHULH-_(7*CX+%mbd6Tza06|!|tDnUp?i3$JbQ5Tpr?= z$V2=V{Sf~~9%@|}dGbwOA55(;me*w6p}e8uBXZ)S$^6mvv9Brn^Idty!Q}sTFO~m? z@*mDS8fu-hJfuGKYqHKEZ>V(+ebzfu>zw5w{gFQFohiQ>dHS6F%hY(;9)0|g@o@Zx z#O<#h?~%A(%Uj0F&&RTkcs+ppKk+=|dX$Gc-)6nxeA^`6*ZOVOCtmP#Uj9h= zzfjMOaQ~C%N6cIYw$yhY@$nhQyni+|U&w!1?@jU!<?qGWK|SZhezf7iL)EI$Nk8voVh?>^-S&X=19_P1!WMh)qJHhOAOD=&3Uz(<$vjVG z;%DDERQwI|E_wEo~NZfkoIan*)Zi?uS@^U=Zij+|8oA<5dB)-rjP%w zddCs_uPMJ>_0qHUUsLz1R{iQx->)*|xBkD7asKb~_Ft26uCLctAN}R`MmGK2&caXK zTUB2^U2O_eF1G2@UY=Lnw0C#miCq=nm)R74aEE{AWmEVE`-?n0uJa{$S9W4)KN6SOm$?`DucG!#dRWF}&`%LNaKH8>G*L8WQdMyvtF3Ur;+wxHTvOHA3 zdH;?6n`)Qk9ql7N@t!g9&D1#YJCTf+se1T5FUHGMJ6s-WocLV{#>>ogI^rSomGz1G zOtq8shxVE&@6h*n>%7$C4W+m0ZnZCDoV6Z#yrJ4{d8l?<9x~48hm13NA>)iZWW4M8 z+x(IKv91tzOxpGS?WKJ%<$dB2&rM+;Q~EqVMgPo{_puN6m9dX0J)U1j-;{m0UxGGD{djJ~?S<6)*XmD5KO*nSzqC)cA42J|UvH>(vyZoZI`oGICd)WM=?{&a?~(DV z`%?YSZk)$d%tt^P&o*ZT|jkEwab z`4sWVq#ooU?Li)DURxe&-di3jE?6GQFF7B>Kh4zJ;h%2bqZdm1p!9~Nx*oCj3zmw#Vg9dUys!u7j-HQD zcHsQ4A@i}8x3!1-ckoBHR~!E0oHv9qdH4g{iah)-#dF#d4jn7(AN~ad_VLgEDFA%2fMWL_W-iASdnmv{to z-9F>bb5izasCmfqPYv?en%q==6gAZR;4c@W4$CSesefr1q;9MWl zKb{wFsDAN0IsJq53wcBJ%jKc^WqGK6SstoimWOq{{rb0R>-K8q*rk=J{;j%n#Qif< zF1GcL{GW3Z>!wdzRN>h z*YZ&HTOP{Zme*t)kT=wLvA)wEh#jqu-?X$3e_=l1FXkiLJfCL5ly~?G`n)HDzNzux z{TBSrOnD!D_C@wrar5fCy)Oz=Zg#Yv^IyxM+RgK44XHQs4t?$uqi^C*x?fED&6M|P zKhFtRAF6(y7ifrnE$^~F`*`~^q~4#De}wpREpOA`C;RikXUhcJ!ZkL0*fWLHv&U`VHluyibdNLgp9phVpBdhw^*NL(L1z zL(Ln@!@Ayn@q*{J-4D_2((@)^%3+&*JwKHHA}3G9F66gi%KPdoKlA@VjVt+FL#|uP zyXt2>LLbs@)~hh(ed|{~?E0a`nY^|k_1E&Q`UkH#=kbN~d+?_;&NXfK$p7>FD$m3G z%xtyg`}g=g?JbX;sfM1rt!lXSd?^nvJyGQ0j`<=FXSWx5`1}W|q32vyZ3$m=PmzZQ zA1d;2;l3gd-y(maec`QY=kwQ9Tf$qe@$<=BLiFbKdk5i-XFZ>`gf~=09#Zd%^pofz z?Lt4i>U7Z$pLe0iL;8h&_=CGj`@+~&Y#Tqvhi_`WGfyD%?C*~D$b6(cJb$jp!=+)<={eb!*&OGVCYH06LkJ~l=*PN#&k%vzl^m9NnHFF)< z)?d$8-H)VQ+?Vt`)BVbZDeq{n^WAE>=!?v=;dLcmn5?%WXUcjjx}N&~yV}F^vbK}x zxbyZp4v8s;E&9Hn&im$@UohhL&NpAJ@%fxOqM7kT($%}44FKfX}p;a_eq^6>L&=Z8*Lo5R0)w9mUXht&HQv(@JC$o^{R zuh&(Z!^205Jfz)!cC6YQ(l7MGSIiat@Rtu4d5B%m4`WBM#om5C3V+=E!bj|vZnw@4 z8)BF1_Eeig?1ns~9mqr4fjrE0#kTtGzuG@-(fno~MciS()iC8^i@xLS$eIQFizz=L zFT-C<*`5z4wi@V6U#&SiRGdE#PU#nGV;BH7^Ph1|-ZuCR?g?^}Uv0kWgu{^{+=!Y67w=0z2xcwde z!}!r();m+{0@q`GFjFqJj9+zJ`aFlnd^fK?R}CK=^!S-qJ>d6MHiw_x>gV+~hq5cr zuWb%xU!LbbL!sk1>AaW#lR3YYi{H z)X#lx2@fBvhJRphwIx&@G5o#PIiE0Vxl?PcKVlV0s)2=9`J^$$V1vec}P3a53y5y{kHMlD*JGsiuz5}$NehoZ>C&q(f4}H`5g8& zWnc1Q>}$%dE)Qi_mxpSn<)Qjvd8j;Z@bU+o@0rRIEf1APx;)hUvOLtd`1+yjJNOf) zZC_LVNB+)wVxsq=SXcHsRl>|x68E)S^}{g8H{A1aTvUZ_0Q@{s8usl>8usqCl`@{pz!5D8-l@o|@S0 zU-uU~!jyOP-})aO@p?E2(bw~P4Yf|X9RBD1pWlDs{;}H&RJfA;_Ao3i`R<92&Km>M76JMsPxY96{g)I7C3)I7F4)VNq4$}cPr zQ*X!k>?0mMZ?@P6;ulw~>k)r=$x`tLsCj64sCHT&s-2dH*tfoZi+#%bV0xaAe1qo+ z$rJhih8ovZ&%M&)Zz_+nJXD_L@=*Dj<)P-GuN!KfS{^D-v^@OyLXn5cV=NDq$E>@{E`0Rk0$m0$|>iI=8^qAuVsJ1x$S1-s@lbgk`(*Mv^Rrv+U*vcA8F?Lk+)(y#c_=&Zo(%RdWk=ql zA-^-#ZkLCe_pU!wyVJfFfAM_gz5@A_sd~7-;CKL24qM^@^N~1de}jzYl=>Ai&Wd~V z&%_UqhxiHd5I;g5;%CT1+12up@u}-?Yu{Gdr{^&_Ux1uf@VsW2@_y$JtP|4{>%3k} zzfn}YZkTehvwq!|n0~DM5?(KV`_-d8rd({(C!YAcMRN0JG0*J?LX6@u8;pv zkNK+Gs-g9JtLgA1@(20PbeQXQ)NlLgJl*|=^k3)c4e5U^Z)<;JfBuhgP`%i{Vaog2 zPx+n4N%XiWzY9~|HGbG{@P$`a(@#_$0iUY;Litcb*^xX1dzxw=&$ZA#^Vtj4;IH0Y zO^44sQOd(y7q-j~_h0u5rtiN{?i)Pox^h3EX3AlkKJ8~coc^o5<_vmTFB@*$+{br<*-v<_aUY)KI;1s_FLU|Xqa-bOP}~K{keI^iJ4uZ;z^it*rw0<;i`M&ALzp` zZZG~`)7Rnt64zy3r(VRSmN(S+qsRE0 z^q2eNHEjp3+s_|~2Wuw0-*X&MJZPA5v28rve%+UJyCLV!_f7SvdBO8%%nwMM;rX-1IsW{DgOT0Ey-VqNM@yDN*z40@MA2R=HGOm<2 zBrZtq_yFY>mWT2i%R~8<C6(R{vY*o*T)|DT8-_%3JM3?N)Oj@PH|Ns6j?VKMQosDo?St3{c}P7v4|Kh7qt0i!PB^{a z=agkgY@cSIpZ)!aG{SL;{OnFCt_TdlT+~s`IR6fb`rsR|6&+0t$ANDw(G?ho% z|3md_WbiKc&!qp%k5GAJuHWVl$5sE(8}$G9E&T2!|9($H_1op4?85y@>|^TsmWOJ$ z%fsKAE%H$L{?J=bINvv!Kf2H9`2)4ix;#v~I_A?Z+W+p|j(?E2$Meq(<<~NBs?(ecbL&rnO8!GSM{c-XSQ*mi{;)LUrsddnLA@LD; zc*$guhgxqf549d!KV;rc9x3xzbo`@z#A~Q^9Q}q`XVagy{t{n!-WGo_<;Ogai$9ww z?-&pKnfuSIr>63op-<|1HOV7}PTu2u!c@PwUrRn=GG6ywR>li5PRMIE*Cn6oh;KtL zJzoAFay>n-)R61c@-}_9AAiFCApR+TYA8Dl-7!+;H)NlTJbd;-k%yXxmWR2n*j7LG zAHL?6;!lwA)&04KYJcQy`m~?*9y^(``|w@G{t){QfAD0F_?7ObVSkhQ{$i@s2dNKv z&E~ot_FrWD_56tA2_&BA`H_ai5z0g22=b6Pf;=RSAPwL~s9>;q`-)Tv?~rqTWnO)*8hFM9pWm4}2OPNK z5uXQ|I!APQsB=b_hqvnc+vtZnxAJwvJC=())VY`Cq0YsuAL`u9@=)h$13!7r=WC|U z>v;c=^E#7yubM0ML!FZj{KB>#bxvw|NW0Mw=@)vT&PlBw>KxGW5c{AX#*Si(y(|1} z;JrueZ>Ic!?=9jFru>BO@Zc||{K(~@{LJN{{Lu1HerkCrf3`f7zoq`R_Awu;4Kt1> zrs}DFaF6}lR6ATADvr23RJ^e~RKKb%%kH14IFjqP)xQ<{eEXF10aJMz?}w4Eneyw% zJN5N_FP;zN`(8X3$oIV(rd;gO=X-nTL!JxcJA7fvVVnM5wV(GZi5I5wDBjB?-!hfY z@t!65oT)s{{}*Z;c~6u0V&*zxTYa|I%c{~}NPk~>wnzF~%iHwvN8UH*c`eBET)ZFN zaJanx&GW9j=go6<$Q$bUJC}zam@M+}_jeR|sOMa*AJ+ADJTF$x=ZAMxGatFD8Wb6a z!L^eOQ*O4buk?@av~hh%zXtxH^wU(o29_#6{|4z7)*hg>&y6w^l#v_;%80!aV>8fKac-2E_l8YH}NatQA5o)m-kt}2S+dUd^5F<@I5E? zAExG8BX6-U`y0lw8t)O9TnY45!UN_(N^fv*#49t;lb@q92- zPrv!FZNAt8Dh>^7-`*qksO4?;#9#P+BIi7i{rbv3neS0~@X9}0s5pN#l?PiMULyZT z9x6| zs-N$p;g_c34c|v2?wBd>h>yqBe%^m)KA8GHz6ZEnGuu$} zneV+auT9No%fnIKocYDW)#jQrM{X(ghMK3mXV3d9X4)mT^?x7!@tw1N&cIaOvGU&) zFGKPg{ayq2?@Z-AmWSjK$V253mWR4;nd`UJ&-nA*HNS5qVh84Fm~yjazM{YKnIj&5 z$hh-8@R0G3ysiF4>9bDYpI$eZAFKzV=7raTka>YT)V#1fWL_W-H7_i$>3Jb}!@AzC z`N8-&KhXI(q+P_5hN_1+LVc#{vAm|wv57a@FFW4I9*#$0ecg`pc|Whp_b>5pQ+~~N zBJpqYb33aQ`d;o#sP|g6d8l>N<)P-i z<)P-K%+ruM_+f9!|Nl#6ZtjsNi7TgNx3aap-V>p-Y+u{@OBEe~aP z%WGn1zIPio*OfoC)Zh4z=aV6I5%lW!Vyq5nsZbSL4%fqF6ihj+R@rR0k zgkx3lpHTa0`%k#`zT!V2>jnNDYJPcqLgqc=8*1KL9y0Hdhnk0$hs=BAA@$;4A?-$9 zbDDlh-VnP;Zu>y3uUx+&@dLZ|SzlS#u(#JY;v?%@&6LBg@mu-7=SuznS>NxU?NRMp zd0zM7L$%-Xn)C~KL-iXu{WoP7u8)08+0Esl?CSE6xQl+Maj`rkZvXwZ62Bq+{KmQ- zsrO$`mHMIlg5MizD8I2hr2XjEWL%IplwYEcpPH;U$V2QRecJ~z4r*6JjT`On8o%S3 zzvQc&XP7$A7+kIA&qFk2%DDjQwL(RhntCdst`uR{( z&mXQ_|Dc~gH1+(S%R@cyxbjK!e%{f%@vP$`&*u^+c|O$Tp`Q11y>x$Pb`FT(C2CX;9L+U|4q#ek^t46YG?d|ZFj5qPc=TeG8KFxa)Ba6?Z+}q2edw zO&s-kSFS5|`76J-LHvWh&;O+L4O4Em94e4Jk@6z`^Pw@`=Tu=MGka!q*mpxa^9wsG&cCK>&OCF`=hqFDr*K|P zK54R_)A_jfb5P?o@X?VTmFIANM!o|XH{=a94_$Ald?M{@Yd`l>ouA0R%{3F=pL71g zKHcN1{eQ!kpDyL0=8N;3Q1fUd{~unj^C_JN_?!yj7sx}+L+gjE-^fGFW6ML$bIU`{ z{mHk&^h0dxFY{r=19Rnk395Z79^BTW`W<;PIR7C(G*u7hM~?4MdYmhT_=(S#LgF^(Q=!`LbD>avXn81q<$MW$h4``S z4dw5gOR*0y(=M^C{kA{-=DYwZk6^wvOnJZj;rxLTFCg(&6z>xaZ?V6*Q zMcmgjbsx{=A?-##q+jTVI(M^PsJLZ$h<(rxiLbP`WBphpJ}3@2j&d%z_LS#a!{H^* zNA906FMVG@^Rc1wOP7b-S3n->9K`Za=OC7c#P7QPE`K7vJDxz@N8`M&A#tXbck2Jo z)8#xw^f|`=I^59b8MVAke;@XFx$-&kcX*5P|Lc?whBuzARvtX%{Ls9iD)LZytINY3 z^FgS}c0uwI_=_)H-ebko9hAxvY1P`zoBjg|dUsk3!kO z=S!jN;Pa;tJ>Q(+W9nvo3A^k!gl81AC6_TeT56R<^hZ;xAL)qE#Fm`OWx9_)- z|2zIc;<57mhQ!5M-ZEa+UwPF{CEh^tU(NT1#G6{)rSJPswDZ;-KDTAx&iSpG@_y}C z-oft|l4nf4bg%P|hAHQF4qNh7;sy0?e1p~v_~0Y1w_(b~HvL87h2r6cryVLhC4Tup zwIMuls#>Y|xgmV7;vMqvcefRJ_}>l`dHBX-MIJt5PmzbIS8Qt^{o}cU4bPb@{Ln4c z%AcRBHiRh`JN30*Jnl7$Z}8hYy`D5ox!9(UeK?oLKBnw4@TMKMk14xwPLF*|*~R6d z?BeoJcCkE^T`Uh}7t6!c+hL#MwC`nA^|%Au3l~QGJjLU}-_UbLYX9THf7E_r|9#ct z!f(!31N$DT9v6N`*Fhf2UIRDox82OYJ>m8~F1%UiqR2zpk>@$Ex0!Z{E%tT16beH3{wuf^qGg7u0QGd$nW-(-!)9R*r~5`k15%Ma}dUX^N%p)uuI?VC$7?u z`Q2`R!<6@nw~Et_x8kNs>x=e_Vaj1!{riY#%CGpnRrA$~k2){s_g2kYmz=lpd#mJc z{LU)*R732g^K#n@zHY9_!(3NvtDktm`6%(iRGi>El6YY%POx4QFHFUW6+eHI zvEu6694}18373b87o2wzUrogc)??y@sW{>CP;tWbhH01B*8Z)v{k&euj$Q|pzcy5y z#IDFY^|hYcZV-EFy>EyeYk8YKfELwaZ%3?IX=QKm2;hj?^{>o;k##x zen{M`uisYxBK`lNW%tiyo!+AUhFXWX|KRl+vQ8rpS*IxvS*MYQtkcLt)@kG+_O0u0 zYaioNJzROcHnmQ#SaH_lV?JlHk+x!ka)3kdx;xxPW4?j-{a#?6?wREsmMd(OMU&e z`mrC+so-BG^G44xdj7zai*5S0-}}!vzN~i@tC4^&hCpm`uN&I2#j zdK_wf9r)EHudk4G6?wy25B;|5(?0Hh5T8xfy)DaS{evm*h~Mb*oDS;?#4mZ?CQNz9 z`s3#%R=($qY?%?_Phvc^>OF`Bb>EE{C1|qwjOsPayvD<oOBzcA$;{<~Pm7uTaWvuWi4pD%_RA6C3j zoN0J+kL4S~p8MJ z?SyI<@`kjF@=)!vJfvO7L$%BDkoMK}w~gOE?Du}nU*a$%&-r7`kC1q+cb$}Fp~l76uW7&1^Aio_=j7}7yYqJReoEsJ%8z-!fO%zpZGDl4)Jwh} z(k}GF&nVuYAD*5p@{oR^A8MZP{08&M)cCvoVvGID^CmjK#(z1-w*CM8Xpc8vSq+Tt zsWyg>+*Ympx8v2u5WD=#-S%H-yGY(pb{SZ`!}fvLVc=0(uR@Kh%R|~t{bAbG-v09b z)LV2t$p@hPX5ej;J#t;jL-~#6q5Q`3P<~^1D7y|EzR319H7=Hi8aJ1R8h6V>*~izf ziCqT%-(}m!#6IsR@e<-U1Mgqx5j*{{#y6DTSRPU@dLivXKa}5CKa}5C9@0Pb!`QLi z-sODq{mQ46_YyDIr!e0erd(|EH}VahAK38R!9w))ynyv#%Ec~y&VQ!#{Sx!p>aWhL zroxnqE&Anr=9c}=S1PDHr8=^wN98Fl50$4_9xAR`9xCox9%>!0ZoS6qxv4zB@=$q# z%R}WGmWRqyeEm>)N~P~GlCPNj-ybPXhl=jF5=H-%KP{;`NM{%X`P46 z2jwdbQ!cjnv*#oG3iM6teerCmAEvxRf0x?N{kKi8xTlcw8Qq`r`3&SdLHTmS*Uc4q z7(KDGeYXsj`3kSt;payhGGA+Xn?CL1eWj`C`wIW-y?$S5D&%?(tSi@rT<_ikJw{J# zyS~To85ewh$9r1zllQk8>fFxd9p`uZXy5%))s*(bruNAL|9IBlQ!;lg`@OfR@X9mQ zz`q==rb62PueX%;L)wo#%yq@K_Ajb_@-Lq+K%Fyl{D+`7!XKedYV8knwrt z*&gMGk+;~#`Q^Z$ZZGoyK11vKo$GsKetvjsnZNK#ttZIC1CK89@CL2V$V2XPaNUsm zAjm`RlOPYdkAgfrA%8(0azEyyBNgXgCi7Y6aoEeG-m`~G{qVIHs)2KJJ^tGIA`fXd z`XT*7FMPrIq91DAwmif>=!Z`~SlSg5Uub_vd^wJPK54P|3B*62dR>oalm}>kGF5Zq zQ+D}$sUd!+^QDa;{&$)DHRQa0*GM@}gZM4`p_-gqAaAJq3a&ro{H$(Yi~m%+nBU4L ziI3)Ocb9xE#2?>V;-iT_Dxcy!){I_9e8fI{H<|ShP7P^2`_^oagC~8yI2B(1NL4+k z`$yq*BUQDuubK+AzFHpYysG;5C7)NB^Sgb1>H7mZ$DI1ExgrmBp5^*$rd|EkXP!?* z4rxE<>gHOoWJ8PN~bFUv#qi*tAK2bg-h+Q;+U+<&B>=A*AH=W*dn z_m=avaNT9)|3bBsai0ov9kI<{wo=~@-CNG@MDCvrY~S9{=X$lgO@E*C*?)50Xlfnc zyqfjE)Oz9aaBiWLhZtXaaC_bQ1 z97C^R%Eiw5bw7sr!~Gc7t8s3aa)Q+eX`~u>&CzPACV2?==Nl>? z;eNZ%!y)tKrfV8{zNoxmt}C|HPye}}!~JB)IX3ry!j!`$T z$|JGA^G5df8o}%*|=PNMfVq1H8zMFiAJktE? zg7XdTcae8^+>vu%MD%?LkHdTWwT=)Mx`iHY7*kN7z+Q|nIk znE!r>S=V8iWo4(@%`J(POI$wPCF`v(E3Uxn|_aS&*!qk2o zzh^&gYW*7c*dt!QOs!ik4|Cm)`MT&hk+6((-WVSdrIEy&E=uS)#aha-SSX= zVR@)?!lB>Y>+?eM)bSz@bq?wBQ1j0Eq5RL+59Oaj@7ZquG{3#mzmLcLDSju9c{}u> z(>?y}iE8L$hkYJu>KxSakb2PzX&3sT&Oxmge(-FOhx8BqP;tWT3gy>se;@zmcMMt2 zAaR4={R~qM+s2ReeduSjuD||3q0TA!U32bxm^#O_Jk&X-%R}W6mWRqSEDv=bfbU;& zKfu&|1j|F+XK;C_{L=F9;bZ0cH94>4dy^q~DBr7WsQViDH+hk%e8}>UdZf?&4U=}D zAAaV1(F;#c7I{cN&<~Y24LyII^D{Gc6kF_F9hANK-WT_e%y&NI-%Z{asvf>ywJ}sX z_l)aKTORl%R|jOmxr3amWP`6 zzJ93r&+j#J|HIV(@%=pJy{Y*=@-@i|cJn*ywBJlQe3Zsq>DSQemDPAPv}R>hmG4LD`vV(Za>U;s z*zha!o?i{MPaL}ZHlIJ5+DGEooIjdcKZjm8;q}whxuDC#Tvu$XAOGj~*~xcJ#U;L< zLwth7Ey^2ezrpX3v+ppKcdvTmvh#0KdAG|$?KAj&clH~wsYms-wTJbc?|9h%q4wRY z-f^VI6Q?S^@49nL=>>pZ{L z-cWff-}@ndHMRcpy(P|fOs)Ga4=>+U^h4#%mWRpM#TBne+e>b&`S{`bh zb$Q6XJ=brU@5Fb0KiBIC{K8^cFGALtTHe=340?9_kGI?qqO8^FBeI|3o|lXBRmzYF`HIpFg}xbb_Z{5_+F zgNMCu^m?TIqt~gkvps%&d!yfW{e4^?J^l~=ll#WN| zt+VVCS#P~xOg$a;#vXj<(eV%x$G(2HN8%jiA#o6SNSs7o({c1)_m_AoHrMSq&$Pb| zOgeuWg36nQ#?JSsyvgMq`O_lfsd>tIhp9Yr)ui-8<(bT1&O1!yv6hF*b6p-P54Jp1 zo@{xjJlgUw^>*Zk$FcA8=e(aYwU6U_MC|9xo3E?}=QTewBl`@>L&kynL-s{!-=p-uE%_xCLmm)B&RS6y+^ z<88|SR{ezT+ttJ`DQ_shU3KY*{nwOVyWUWKzv?Fs+y71ZU)tB!{(Wjc-=`p6n~K+b zzk;|8iPys`AM8=%I{fGb$7@q|;(OZI3t}(I8|J!VTm9J2*HJr7wV&?;F@H_CY91OTVD{!G5P<^u*5o>3ebRAJn+>J-LST zEAkfq*{Al6Oh4d!z*HO`x#XDh0rT@mN_-2yGVgQwjUn;xa;;w>aZl&=j(D|DPP=}-pW;0B)ZvEIPkGn+ za2)^8y2<=AH7{8Id>#k&-s}LJyYk3E)Nw4T^=e9S{~-Q zVoUwye#|Ei_&jR}K4VWc^yhOuK3C`0cklD_7^a@vvpm#ueJ&66e4yna=Yw1~)H#>s z;qPuM@=)hwmWL-!6?v%f8TyM^kB_PIXYOBc{%lh3SI_$SM3eaRcSlQng2X4}A?-## zq+iIx7o0Epq5Q$}5c{AX>bWAfD}0*TTidtA{=M@e_Pv}Nu^(3aY?yM+QCi{$_hUF; zpnmf`6W%Xw2sw9@|M9%1DLb>D*R;xFO?8d5B-U^+fRxi2wZdLXY?r`XPRY zJj5@Nhxje>5Whwq;%~aY>Gd7rC&)wQ7y4oPA-36@_%Qevhbx|EGu0oy$BMt3YRBN0 zx4Av0+Trq0_HlWrc32+fy6{oef7)N|!rtH9@Arox=Nx>usv&u6wOsO7(dPt`H`IH> z(Qmu{qOOnMdVdVHZmc?etVgXYoQrvX3>D`r4;AMu57`%^A8K8(JY-*tJk+|9`rF!v zzmoquzC+@>_6H4#@3p*5-{Z&r5Pya`7v%ff4Ih81l!rP;w>(rluwJNn!hVqXV&=LX z^M&?1|5LrD@>lXdKbHVgPP5E`7ca>iG_Jo)1Rl7DOeVrpN*c@O&= zQ}=aOeOvP)RQ|Z?AC5YIG?iCc9&+Cp{qXCLF7i<464nd9b)m>Z?q8!{lYQFInls+7 zLH2Q+Pc`KKCbTaPRgdK%?LZ!KpBnv8`LwSaDj&8ykIdde2$>=<&bs7_meuFZ?gVNb>74|G}Lpq+@Efk@{aTAhmWRacx1TTZ8)}_lKi3dHraUAr zQ-8=hk@j`@`_EK9j~Efzhm2f#wjt*fl*6w1!aC#qg6MtBU(Yp6dDnhok$9u$57-Yw z;>_?d&A(9hJ$Md*`yZy_p5-C&2zmGe+loB=uDK!)hmI9_&D7g(KR)zy#ec;uQ|ryp zPaN=iW2Ri}vX7q&Q~!KEuQRNl}3IPY^l3yJeOuWLwtTFblY=X;p;AE(QRkcZ^hzQy`kEj_O-O%`Ox?;pLhFBwSWBO%0EKcar|ZX z+TP|%wibE#`YVe(eB)(B9^SfC`MTk|P8E6h2m6aWRKHw*D7#oclwHQJ*YnOH^}K$2 zwJE%LTQ&Y>`A7J=xgrl~C;B1%KtH@@ebEnPSIa}}fqp1EkH6xC^Al6{jy+rK?el@P z1E;NTsvm2|<$s~B8+n`lqWo>myS90}OpVi;-&^PLGG%9%hqANFL)qE#Pz%&9AX*W<0-4*+25O{-HlUvA&Eql>Yd4U+58ksO4??`-m66DSu;r zLgx1yWHA<{k3z#I9=e*Uwa&Lixey zYc+nM{KWE5_Dp?k?O{HQo>qO#TPQn>en$0$#D&kD@_aCf8_2^~A1m_k+H*x7=DPjj z!DCOm{xPU}$2K4CQF<=#r+=;dSp6J>=)YR_2vaV$^&fv8`&d=Yu4NJ^7|N$7@q|c6q4raCs;{u{@NYSRN`)TOQ)C zZ`)S<6{g+}`_jJgCzk&+r9b{-8vjt&9eDqWn?C(pdsOY$|C#F7+CMt#{+a66+V`Dx|4jAE<)QlJ z@=*P_AyWjpNurtCktbJF%V<^Pel_0Rj0x1IL)*(Ttvd#Z_d>U(41V`r*~FHQP; zZRYn&UjKjF^=J?K7ruLE>bn^1Yxq8fnR2mB|2XtBtbuKya(NjL3G<6QgbvchS zzjdL=Yw}#j#OHST`3_U(i4&i{)aQwib41D;>bVix#q(e0%~zIsL!I-nuJinfnRbb7 z?Wg~1wl4Ym9FXsethw}5L%!P)d7D1tvHBNJ`1>p7I}cT>Z{OzcIYYj~vii8bUr>|p zwB-NWuD4IuV?Cr^NPAc>L%zc^`G<%6Js$I+Im`L}AOFX9U0fdCS=L*=A82`fU9R6& zKlWIC_*gY_>{8S1KBDi>(0(!Huv7oF`hMK+@2FObw0reU7aFDZhtLrs~>+RpJJR$jVE~)-{&@u+)_>cg4$J+?|M%@ zU*Bg5hnK3!=icS-7n`!1_B>5(U+Z|b@(4^^+rL$%BDQ0=xn{G9gJZ@S6fvo=4! zwa7z#2ixVL=85%dVmGeePT&*8! zTr3ai5Bj0}W9{KN`-dre+a4YM?fCS|N6P#bJ-=Tu-_Y~BmbZ+T-zS^6|6YGr)s&ql zE-ctz%=72GpPLDnwEiq>zK819#KSx7zoz>6g}1zCUh8Pi(vX zBJt;W%WfZ}Uu#}`qDT4Tnyb#(A0hJ;c|+OH<)P+@<)P-0<)P-8<)QkY`rF!v{ny-g zqT2G~dkd8}t$AR<`IDJ)u}gpLfeY1^CukoGl_#xTP#zYhTx`?FK5I8E`Fz>D`dqbk zi{e{&`%ICCcPtlq_~8>p9)5hG$iu(fUgV+9tJnU>QJ+_vFVNq0{cY{TKIFT6f7zs8 z%4^*}m~ydG{}=Ut*a6DEYhS*-$CUT8kL=-kAo@?;-=n_!OFy=Rv+6JMaL0U+hnLD; z$V26^)(;;$Q{*B2tLyLTALk`}-`G_9I8ULSX3E7befCX!&(vgmv|rjBrd({(xBq;6 zN3jp2-JhB4k$zDg(r@G;c0nFuH{>C9MIK`R|9QUHA7UTm{l;JGpT{4fuXx{(@vr4= z{lovrd--0UDgPsH#sAEd_xax5?zaP=~hO$@wzwLUohj`C?Gi5L0Jnb=4-Z9_MXP$aKLE@q2YeVK)E$^zIeFXEx zyz#8_dA>JKUf+~U-lb3f_}-$a@8MwwzK3U~TrTHdA4d}TaM z>eYO9{V?Sn<7xYS->mEBoZEK0cwLXG-{pPgGy4(dlPUYNAK^M?%Eiw1e@pX04G4Rzf$hi~%tc1_jG{Wre1 zYaZEOt$E)=72m@(^&MQ7hqfurF}|do;XQ z=NLLi*b*K-Sgn5G_G(M0c)t4lU5@9b;*I5@+Oztf<$s~(E$0!;UsLnn@=$*4`opwK z?CL-FkMJ+^4+bmlFKrG}-r--i-=7{Wc7oXNqx*Zrezm-%e(zsbf8SxpArt%k;DKU4 zNWYMWTE{F8HSa7BwGLVyKL3Gg_4nUYZ3$m=PmzZQA1d-t@yYrj<3s%+<8tww*9DVt z*)Uti2coC_dPBxZ`+1KSyrC-ckb2PzX&3t8Ri}%7_`C~69?~!LL&m$dXN$e7UHJPA zBh}{LoG8>duetG@$J=BcF&{(b5%Ms4V%zoiaebX*67Nmfi}Os@QB(Fw-{(H@EWoNtBdx8)&mA9+aJM?WO)BM+}WSL7k_zplTle?IrV@ge(*#|3*c zJ}~8Cm;UMx>;JF=q<gLirJn94f2u*^@bC=UfsU7_B&qazR&bu?Jf6#o^_q?|IE}(IqcFW zK2G0%!Eq8j;$4_>*rm_7BlkJquK99qS2N{(^)Au1mZKQ{L4-{F{7iT7G28uh#s2RZWNTJM!S^P=0B7D8F6v z;alv#ru=%%N96CJ{NClE<^}mQ^TX7AvRPGDci_^$rUo*p%>tRIZ8sd2SD)VR7l#Qx}q8du9}GOoxQ*7bIc?>^dh_?XAT zBu*TWKZhEJ)kkMbynruPeuzAL>T;2XPn;?8kT_Cbzs0_OAC~78?f($}#2-SC!YCOY7ovqeP~5_RnYU=n=ovdSw5E_$BfXzob0GFOi4%CGwi~OUWD7^>+E^R^rDF%~$#( z+Rn4OK6V#V-W7lFZ^u{pp((%Q{>gMGzjb*izqUNQUh#N~AFKQDP;rlQn1(4AyXxos7yp2aFXzKy%KNQv+!w)Krs`jN z|GeurQ!cjEe_ZWf`%8Q6U#8~u+L!J0_?utY>HA94HD{i===&!P<$v5)!T(IPpZhDc z-;^J$J$TOkV9GDo>i!h|37IF9H`F+|-cbIR_O-R2e0TDv&wKqcwQlhI9P5Y4c)U&a z4YiKs|J$yIKd$`~%{S^7@#D20(fkNCZ`OWl#{OYy-dJ9fI6!$r`X_z&57yW1@~1`m z^J}t)`VH*|dcLrs>^%7!$82v?cI5d$>}hJAyF65TT^_2Pme*vSBX3yO+i$*ej!u6d zcHur#m~z-=U(XkvKhu9d&w@VBlhjOkmwg(2%3ngod&_IG4j^x+bp$!~v>| zuO{`LIbZ6BTF1D5(@^V}J{7Chd*+^a{5OvDpdctKS}@0l#5;Z-0$%E0kO}^*7wNzQOjHOou5s9LiN*5NW1S| z?vZ{`9@1~*A$CC?VmIXB?CsU$rv|I(aMxmyhu5Ag^6*u+6?w?GaQ%?+!=FO>tNWAg zH$?Bg`5v!7TunZ(-Rrxlb>H%kdeIAM7y99|7m9xP%o9Z((l7Kw{E_yBv1|MIm*=ex zT(JFl{(|eQ{pH;~s-Cq!zfetwYKP^a+G)K|{jfY#Kdm3i9+ro)m-R#Zj`cK@o!8!U z%=R`l4qkUd^lm(9dz;kDx*XCje`{Y|ApWJ4BQ+6Eri^~3GN-y%3`Bsr{Ogw&@&(}k;239&_076B6h7|AtxzCN|D_JutOSSRP(7S>&PC8OuYhL)NRw zdWXEB;&$q9Yu`S#Z~SL>c)f{0dbz8>qFsd_B0$vQ%LL#-poSx-#m0l9u# z{lr(dPxH}~Khq!LhnaG*OP}{sS+5}N=e^W0<*-YC{B!$D|3&xni?Rd#70FW~@2f9= z_W#M>tQV%dkH7Q&FztggPkPYr4~Hr5YCrxpKC<2C{pQu@D&DW-`7TrE^ezwYST5z^ zhffrF`0<4z4|Siw`k~I-d7qDScT?y6slUZOUcbg3qj{|Un~DRxFT^@#Do(gOR2*@6 zs5oPJs5oSKs5mut@jZ@LrsA08q2ip&L&ZbOL&Zs7Ka}5%Z7lKBRQws+EdL78d&)M) zQ&ZzSc17j*Vk(ZiJfvRqL)wLYsCB}6q1F-0L;8n)sCnRag|VyHVt>ce(XY)o{y@)* zZ!I?@uZz4z-|HXmp)-D_#*Ozf89!6w=JHVE=JHVEW_hS_vpiJ4CSI%kXehf_9?EW( zhqAxrp~lVE4~ZLk-_!8}@_)B#JVTAw#Jdi7yiARg?Y9@wU_D{v*_VPTtpP$o6h0^X{Li`;6F$`;8{`zESrt zL)`}*{cl=NLfsFwJfz*|hx7~m@D+N$75(s+H7>|Q?1O$NJG)(>{L}3hyT)(i1DcPl zm!j9n59xe@byG}v*LsQnj65QLL0@z{94zM(qhiYY>GS&wwBM8+`Mm?|X{KE4YXA6e z?Y2Ek^xn9>=tJ2l@-BViAAV|T+=(|lzhI_Z?9v~7{X&^PP;qhe&AWSK{?zg={qg&k zOaCG5|Ay=l(*IiCsjocC{euJ61|be^XA!<6?sU$WomJrGmxG2l16 z&tRrp>|4L=L_e_~{co7^KJmwXM?LaC-dDiyLf&7BysLilWRE|T{m7FWGXAx^O@E*K zpZBGy$Na3`fBw)mzi(mcJq(wJ56u;MC_7nRllNhGA3JQW+sA(FzuZr#_kh@!HKhNw zyrumm{{HMCzwa>$^&ZCP&+YF~?`gO^)O#G3hj*SS^6(Q!iah+>&T4e8&i_KaXJq|Q z?;%+ps{NMN{AZ^-z*WKS7iD0_{*RQ_I*eiQ#1%C7ug0RCvo?v{ttD}COFGHDn3 zp~lU6p~lzpkba>b%HFh(@i6s1i|r@2*uUf#x122R=Rnmn@qu+cs-B5j-laeK1OkmiYt8kJSE#%%57`rO)px zk%vLI`{=obDTi(P%)ik|#arSmRGu_CqBtBXk8*kVv)hY2e91_WhuW8nu3PW@iJ9wm z#Gm7=e_uTA{KZt>GV+yMoWGb?Xo`~v&4A2bz@M!q@Y zcw}l_B5!4VG8Nxl9?IUXH`KbH_O;lr>?e0Fc|S7-b>1;Hd$vcd>toNn&+E9kbWf4j zka{!lzmrR!OQ0Fw3hdYlHd8qTAv0bNp-ebzo$95mJ zznIiJr}-G_e1B|y(&zi;SXJa9?MJ^R=lbY3)OiegoX42*L(4<#BYpB4Q}c~>G4IUJ zslBy*TkP-m8OQWH9**;(m&v*zIq!>`inA^c6^C6ODh}oPZS^~U zlmGF&DgMWEUie+Zl#6Zp%s%7?Z#-M%q541it!4M$%ym2ZkNqb0%#`y;k-TZ* z#mfyj4~V?2{foqp-#p{}p{ae_$QzD$|7L1m=kiedK9`4zbC!pSbC!p{HCyE28`o7M zZ=Lu4)71Vj^>?+;=NbAxQ~IO-=e+ezT^BoXzrrNWA`gkf$V0W;@=*Pks9Z zXQR50y7t9xAR|KU7?|JXC&e{hI94CN?W?43+PD|5cMb zeq#Dc=kq4@aDOMH9mqragFMu_!}WPT-PHPId8mDi%R}vREDyCGvOH8CZ#(wM=ZU{t zzJHJ7Fz>ApXB(zmY#FZ#e;;4-XwOGe^JV-|k9fYAU)WiVUo1bU$#X@lmkl*fy`F}e z$CihRPvaXj{-Nga__X|@CV4jUhKeh$H`IJh``X%%zjKaa|AF`4<8zdT_*E@$(|7;5 zZzlVWL-LAe9qm!$GXBg{{=E-Ver0)0=MQ@?b^bIC-QoQhQ~MUqpV_~d z+SjlivA;1tzEH};51%OI;T_9G9_rlIdg0aQiaboc9p{_))7X#7q z>o@sYL&mR`cjD#>inX>=bhLb*TH5p&zq4wq6Pxm>6=<^B1%Z9Q$a^i)l z_1pD__($Eow)SIx)^+^X#BN%jZ9kZDu}vTU;(ibF$HabzFBJPh&7X1I7iy?^V|mE+ zk%yW$mWP@*mWP@*mWQdgeg2g5HtjQLANvaYg#AlHy@x!uwCwkh&FAU7>2GJMEur3H zc6q4xoUI?~J!s3r8>*ro%AYL{72m8M%HD2Qi2Zb)WB-HLubS^sdgCkiIzF1J*YZ&9 za(Sr!Ssr3{t{Y-^t{-A|qeh= z!0$hs+?Pila-SP{sJzMYFxTyu?_9F3v>(#`?PZ)y+F#4t+J9W_CoXz^LgwejV?E06 zh+p`h$vi?Hsy^~L>NPcg$oH5(rsj{!!@~z1ANf5C;v~Ow;rc`QHTfp~ZQ|c```X%% z{l{K6@4V0?FTHtt$xor|Klb*K9%XmSL)qQ(P!$3HTL}%depoc``!^hA8Kk|SsrR$xjfXo zvOLthvb-kq3VFl2Uh3~^-}pZ-6#GE=1?MFViLd{$yTn(hxM+Due4#wNRQo6NL;0oU zq5Q`35c}8lx3!P)9?tJB@=)LJ;eI;b?=f@T_V2ZMePCYmd&rRQqcJbTl*6v} zbHAPM8<}6#eY!If)l~RTx*w@-S_kT9a`PjZ?3EB zx7CmT6W?ur_#KVIyUO>e%x^0GYyIGRi01p&6?ypX*-{?9{cw?oH=i!@aABm#L&mqR zzpZ`v7kL@q&ocj3>$~zTz6WTgyyN>>$C=+67ru`Lr-qJsd>Rg(bbOo&uUEWM{yG(2 zH{$$uD!f70K_22K%3tj-5I<7u*?4352t;QesZ5XOnKk-YyXXZvJaBVO#zC=>O;S z```MVamc(H{mTA^d>5YkcMZR`-tvv%XS9Ds9_o9E=x+?`>vH|J`msN81^+YUcf`2L_7J+ZBS zi}X+Pg86T1{*OJr%zsn!9)D&2o0|774>j*y9%|lO9%|lO9%|lO9;V)o_>TQXpFCIb zeK1qs6C1sJo4+S!rd(|6-+kEcW3!IW=9_hYQ}LGHi#Oj<6?u46=kLhF~pPp16Q^6=qfMZYHg%kx$te(L`Z<)_H;S5tm7^5^U9|EAWF^rx-A$JJl* zCDt2L{Uo2Fzvj(XdOhD5>UZ_Wm-Ic(Q0*Ll;6eZXKBS$<8_IvjAJ%sdYBKN09~#Pz z$oc($Q|))XVcI3OwV&}OU-i5YJx>%*!<2W;mwnPFk3jD4mhrs--LE^ zj62`YGc{kZKi?BHQ!aMdpM4nd#Z+7wefnkP`#CV>Vn2P>H}qL2*^f0$d7pfW?`ds( z=R=jL@#4E#jGLKqv88^WmyG}5>EizozrXlckN73!q2{^ep~l(rP<~)}_{qU){4po} zy;@V>v9&z>`lE|H)OT<#51C)oU(@qS_s<$KuXKOS^GoDAy5molKZne>A1i)r%5NA5ds7#5TQVXB0&NMDH5P!9*uf{z8Ij@SStps5*~|2 z1c^Ka-|KtLzUKaC@B97h`^l_(&8%6oX3gx`le5tenYXf!csH59t$#cG@BhEY{UNU> zkoDyM(R>J5M_zQatS6BC-~PU!OuaqvxWCP(*vWjN9~b<-l*<`wIssd;O8sCn!1Q0t=Qq1HvqL#>OJhiP}j z`nX8%`I+zLF|Aj6&(C+~&6JOt@7kAL{_D4Uf9CIGX@6Fka-Q9S_&bM- zJp9q^MINSIvClr9yRgs1uV30-{0n8*B0Y~)$}Y=8*=2bsyDSf7m*t`CvOG+?J@(oE zJlAOd%>Iq%L-t!?%KP@$=yQI@_?Q|$&hHpMGvz(ww~O&>l&_U9P4+|Iy`$`hpz@5% z!@u9@_3X0n3kUuF__9!W_Wb2*y}vS3uh=y{N7O#&V~mffIOlwg`Cw{ah+o)0ni?0E zhguieZ?Ha^8eg8@h(A-~>+;^+-^72xN&ZGX&Adkji2%#`IS8AbxwJ) zGUZ}d{~15lNk0d&ZZ2O{*2$JB?|VLE{AicwH|XbBwO^_H%6(Bk&qB^kkcY~vmWRr# zmbXm3eb0yc)jsd7oL?b%RqIFy*jMpZi06 zXB+yv*RR`Inex8xr_;Wl%jp--=k()xeSa%Vx!BeI5$W^%!uXl;E6*+X*L<1oqbq+t zEd2LteZKXuP~*Gk#43-ksqyFhi}5$LZ(j6whZ^?Jrt%W`lKf<9TwNY2Pr2PNc8yp+ zS$Di&hpLzLq*C=n-qnBncfl>EoL@}#*|#eHgvvXScj)`Ro#&6n&-~Omua|tEopqCW z`;gzfamc^lU&_Pp>ii0M%ges^R?92Lwz^)v?@s-k8&PlNr6)^yxaLrihmYJ|G6h&E1oMV8Shr!tABjF=tG@Xb6=zq{Z`(quk#GnW6m>t z4yN;rFy*7xYyQ8f&#&O!`y2j0XC?Esm3OtjpZWULEzb9lee|MloEuW-ocuo#&O1%z zb<0ELb(e>e+TWlb>YUT^@cR!Jc}v!TwBKc)=acS3Fh1OeU>Vwe6MHq4vF&huZgA9%|oYd6;%b?4Njl?s$Qk&%9r+Bwkv1mw%3^f4tu! zE+Ov)IM1rodja~*`vFt$1ul5tTE8DKHBT-$r~DggKDj*9c{%r)*-x1~ugYKSr%cTg zmxp=|b^9%2*Qou|;^!@Rd`-m#_x@NvAbFqiO2wVaL&c@#q2ku^P;qT}sQBma?-2ix zxN*B;mwz39+E-lql=Hm5U|r<=zcS@whkm{P;{Cnuzj$AN?%kmOPC*~<^}gpm>;I* zh08Vm6p8FE4OKU3>S%gY{hweMe5DsH%!MSe6DH{8!6eoVa&y5Q$mJ0F>fJMK?k){?wO zd8P8O+Yc3YwrfQEd7aaG3)xq*K838ak$1#jdB3ZBi_AanDY9O2e=$sX-~1&0(epVM z)P4c|O6^M{?`xm?`Oa_fKb7Bff4`Fa*2=r|_mls%9y-s9Ubo(){4f8X4>c~VkCpPf z%R|;JT~DlfY{)IP`ZQ2QXuL+z6+4A|G)aG z@NricdH9?gi#)vH+9D6NuDJeC@yGuYC;m*{$KIg%7ozv;r<+xw)*1dkI_r(8=XT3O z+C@LaF7(64Zz+1A?pIhI(l7K|#*d@s1NY^~*Cx+NN9W7)5=^<+F9arfd|;o<`{);IeQcR>*riXr@*dv%NT~IO_sf-9XDknuw=EBq*DViO z5A{CZ>mg)++ScFIzVpe}leVA#f8cWh{r`awyD9IppM1doKf6rl_>lJw4>`L~dD*uQ z`~Ry|-mdv3Iqy47ox}0}**Nbr_1?tg;qNx3Jk3c)j4MyB)8l=Ap|&+C@LaF7!jqL+gc_hn9!*5B-pRB=)sr zUSfaW{3O0PSGoLG^qdA?q35~3Ju>90^}I)U_>N=EqJN)iE)Ung^DLdhcd=$opmVTKc`S{{Ku^>+Lzu<^Ru+PkBFSYF_d8n7EH% zzT;m1-HXdYy%#+H&?&zkGdw$v5YpDEA*sz3=4w1ph$tf`8wgADsVrK4HILYQ5(p+CWS(+f7^b{$emMWWLh-=4Je2=^4i=`oXMD-Oe9wmZA$eoTbB_+Gyu$Zs$S^EtDZ)*pXANYH_mGXn-q5NQZn0myn_O4WWd{2%3K>T#!mfb_DeZB)h|6zVNvi~1F zR{9C)zrMdxN&j1UuRSjsly*ehqwm{PV#lAzzLvJ9{e4$`j0fL8T6M`ah3GAj|H71u zefs?UZswz@yvyJBCjXi#@0pMHs{OGEz6YS*Sd3c}w!y*g*NK zCHp4-KE#eyCyPB$=d+fF%9H4`zcQ6aEf1AvT^=eATOKM;TOOt#J^9n|`>XPg^p`@d z6H6bveMsf8rB7Jxyk=^h@$W*}LEMx7&D10Ij?evze11A6vQHiR&r_A0t5QDd{wd$R zz;C9`yZP<~=iO$?#oqQ$oNF%s$vgaa)7hu+_sA<#E_UhfB0kTmJ;eiLo#H#RA#rx$ z|ENEq;sQDM`b>?h<)Oya?-JeD@pomHtD;A>ZSzr2nnFOP~C~__E*eIO}=e z_JleI@Hn^3@A~#fN0={@=*Pb-gNyXt3tIL^p>#2fjRcnMS9lMk(b zYH!I8Q1_=-fivREn2mRd( z?#J*Q4Eybidxyk-r*AC&gZK@3s63PU`|KyLF`mwIibLn8mMMpQ`h3sZ@gS1lm+E`p zVaogTcQJmiQamzmMV|Bco=~Oc&DdKTKku2ESC+TrIgj#6@`Lo9A7EQ=Up`~}mtJwQ z*e}}dO@}INe=G0n|Jd8ll>LIpx-j;hW0l@VwDMkktxvSWcR{>9YJCb*-siu>uluPu zwVvWPO!>(EvyWyzi|DbR4pZLO{(i;}yP0RKv+RTPeVfqxA^O992r90TSF+EcJY=7P zJk-9z@{oNF@~~~U=RC>JdrO~ky39|g`MvaKWM9bqY~_99!*|AsCrDiKz3(vPuvh=< z+nhi7exmihJ*XtFQV#p{`K~8&h(6zS4O8Bu@AZ!F58@wF_j;H7<|5zkHI*lpym+(o zg{gev@=*7NtrzNEvE`xmIo1o6cP$UKpR|5U=Ess-4tRc;ikBs~?r^+7;)L%jR-*UT zUEYtHw2M5%F61HoLmn#LTyLm2vpiIsSsp6REDsfDmWT19*f(B$Uzzy=87IE09HxBK ze7f*ctDUzX?`bYPwJ@ai1s9$?=lzpOo<<&OU*Yml`P1@HdDHTi?0=9~Dqp7kj{cQ+ z((}6G3E~&#MTp-h5AiGV5Wgc285iUs<94t9KT62BA`cmNmkap1vu?u;~yg@%?ejyL(7y2RbguOlSM1H;S zX<8o`7t!(XEakt)>EDoe_*wNoBn~JKi38*zaezD|4v@EW97tZ-w%fOU9KpVKjoW@x zalrRDi3ju5dal>`0RP{}eBjpR!uNio;r|<%8m9|yTkP?Ij2GpVJa2t$pT`UOIZNjY zm5MW$hdfWweki}jzK(b<=ZU9JJHIa$ombDuKjc}FyiWP3{7%0-ujwb_f_*Jh-Z$S_ zzxh74ezMzS4f*`0)L6?1zrqH}9xSd0+qV?~)BoS-(a5}i$U3caL+k6DFl610yib4eH+K5Hrm6RRiyzqR_kE_`b4K2$&-Yh(FK9}i z@2>K`&`i15rSJKvxZ?f+amKm%g}=CSNZp&IUhYSmx^HKBsB`rTZ+gJz>!$AIUby!@ z-_JF5f7j)q?)739_j^s<`*pkF-bIdY?gta+)h^lBWq+L?oC7jXbY72MWy(jK4_-L2 zqns~7^yd`sA^NSnuYKYN|C{pr62%YxH&fo@|6RnN)+hXK%Ae$a-dC9N^OAqlc!c_Y zfG!Vp@4@o$otjVRhpAWWv!DFM`)N~llh?4{Ou5*nPh9wX7UDOJZzcX~<$e0>|7hRD zZ`z;Pe=y}E+IN2YfqR|bXqWs}nR2nK{rzozz4S5r96#L4y6{!9Cp>!0@x=Ws;)?%Y zWO=B2SuPJ>zo*DUz1O!q{L$@29;RKf%Ra|9`$hZ+@vrual_?kd^!@z6`;@Pjaap40 z3;PGAyyyMG5#m$h!#p%KFBu=^r>S{Me&+tZsd>HR%{O~~o0|7a-f*k$@0-dGE)R+G z!{a61A#twtq*8gu@{o9@{V;a*YJcbWgv6)zSCzzPEAQ3UzR2qrq+i+}RjPgVPsn@qS^sGV(!SRJ z%9Qua55~vuTjd{<{y)3*1Jtu{@OhmWLV_%R`Nezjv# z$Bqq}?Z~Iyw zxVOjp;B{U3y)xxupFa7+?L*>U`K6NfTX~oMF8P1#DR;R4=DK6e*w60u`*-u|bLD+h zc+G;}6R!#%zoi*_{@P|$_^91Q9?Ea2zpMS5)c(?MYTUKHn#x0*)03}E)x-Dd`Fm8R z+HrY!!?g|HvF3i5IeulSC*+>McN9-y+UZ%(iKnIe_jo-tHGg&Lsrk)$JoDR3x!BQuc|Z3f$`6`vOCk2^{bi-*>C*Mb zJYP-CJYjjLJYxM2KT&_Ed}RGl`D^JV597xX`D^K+ruY|P|9ejk ziT_)9$9R?V^?w_b_=Z1qe~I6axNqfM`umAj)zALP)cU;i{l~mNF||Hp59_n3b=l>i z)@7H6%3GF)T9+*kwJuv8rrqxK+3PFs<7^*9?;h=+Lglrkr*3opGxh&2ED!bnGF%?& z|7}*iHQ*{X!nnZ{#6e8%abACe!ChxiBmQ2E^Uh4>%)yZ!I@^8FCyQ*-Aw-yh*VJNHP) zvypejm-CtOhueqvL-VZ?yIOgl{@C~3>i9NUC#IXSUO>fn#m-83=&#UZ@O6FH9 z@6x}T_3g?n-d~tcz03C{R)yHZeTxu#uG0DwYTpt4uKJmOV;?H}M^pK2>`#=BLglrw zk0}p_%1f4q%1f4q%1f4q%1f4q%1f4q%1dd#&pzHu`}qngzPZ0rskrBTHSuq1URWM# z-ncx}ys|vhyt6#iytX`4T&Ddl`*z{K`xkk9P5f}*WHP|1AM!Ithh!hq%KP*$yjSyA^_z^( zeI@^xDer0D@%KH4z21*O;%>=~A&I;5_mud9_z`)CzbOxiJLDm8hdd?^Y=ka#U=Lxh)+}gae1hGH1?70&PS%= zmia<{HQ>OE#>~wxImA70T>iA%@r-zJnhbA{0XO;%BP%a#viw+Srh(@>Omg<^zF^~GxjuV!Y2%x z@n`O6*0j9h?7ZcbU%a)nAI2`R&;H53TWtGH=}rFCp5}^B^-un}>~BfC$SY;n)`EByvZ`gmP?2dhX_D_A_PTOxvf9gZGTi;Z@ zQ-8X{{)e(t;rv7}Z`)8{EQ-3w@{zLpid8Pd3c0<`8`?~BW zey9IL`uaU&+^7FS{%o0Y*riYW&D{5J_uo|iXUi(PR|IBv|y8n=VQ(h@M-EOG<$G$H6_tXFTw%dN_exH?n^dHK; z>HAj?srFqSK6YD?hno%-d3eP_k+)=CqF*Wd(tekHyJX+&%k+1>)gOpmv#;DXM^&nsCLIB5!%cxyJHJ`3rsgWw!N>8sEXZ{{Fn|H1+$z%wgMOs-D5*an}c#my}m( z{08e4SE1}5Jfg&xDgRm?Dn14eJK*?$%ulx)%7250>+j!(u}kdoALBc>TJbHpDZRP1 z^VWxJG%l1^YTR5NYFsT3)j!Ka*=>2K{>?qS@%X^nZnu4Yeq8psv;Lha|1SINBKz0; z`l@ExCwDeig!1>YKQ3`^YJ4pZ|7&s4Z@FgT;g(k_F3?*ODsIpxe$449>#qqlZrH{6 znd;xNztcE}>i4p{PkDZr@{8r6{O0mdeziQ5|1A$`_iyiP+&;v9>Th`k{Ze@)ev#b% zff`@xuaw{8->&#rtN2*H;i&x$>F4rCYM!-ZUPRuZ@9~@XwL{Ii%MUi@`kl?hi}p3^ z!bcBEdHDFVMIJusaFK^k-&5q_b*q|*U%#bU7d~%sk%zB(c#(&%ohtJ1Z40IT@ZGZW zCA*t-;qTqnOuT%1vo1vMwdb03;eoMc;>`=qx^Ul?A`fX7{Sdp*51-!@{qWiIMIO>G z^uyKri+y4IDt7qa&;KhX$2@+Jaa%EWx{~pWyhGpPx8g-7J^xI_&x)5EbNrZ!uN5yo z;rKEYe=cuHTvA@C_(e|qnu=@cCBK-8dzXir7cLJqZ!8Zrzbp?m?^eA0kmsLCe_ye$ z^cT|Z8&?mhdA;JT=RChn&3ns3>_@L9^9p&T@(OyaAExq;LZE66L=ue9G~AM5wTt4n;E^4r8Kws^fXlQd6z!pGx<|H+zzDO z$!CrasqvY7hQ=$@xL6))Tr3YYE|!NH7t32RF32m@zqH?BpXdL|cc_2*ovHq;d~1nE zQ*piWRSS-9Q{%Vtw=^F@jho9u%^&NvWL_b!)cB&$_?jA5%R|jy^qI$|{Ec1s-IV`U z{=WPl%5N**wb}kNk({@%;Pvm{$chURQuIe{6eiwaUi^tEDeHXp| zYTIXO++5z0ep6m4zahterpAwY89!6w=JHVE=JHVEW_hS_vpm%JUG$;V9zT=*eq^ll z7t-#DqeE)^F1mBC$IsNbSsr3PdMz0@mb8{8K;ch^g_5e>?o| zzbA9?Cy(0ykbYkLm!-d^?7aB1>Tf7JUEY$oLSCtHLC*M?YCrY&wSUPS8gKp1l-?z` zFIeAH{Vs3GJfyr*b|J?;Q}$nSLjG!5>s9?-?Xy3decqVcH|4k4Ur@ZYq#epD)edsn zGc~T3hZ=X6hl&f!L&eYR3uJ#w>~p(4_OXAS+$Vc9FHHGo@;6KToAQgxTVf~Wm9i5# z_L^#U@<3@H)_PTchkwfR{f_Zw(@XUn0DoK02iG@4zVWJN;yH_(P2pSC7J2ybYl=Ml zKZ=XfRY7kQUH^Yfw?9CrU9^X8(LsehsRchPU` zasN!&Z+WPB>GDwX!17S@#qyT;7kQ=nm-hSYyX3dzAN>wu*CnqxHl*ykmwm*~vc*SipNYT!ui_;n-+pVpI9mFh2Y;@NELrT)J5r~X^NmmJdm^!KQ}Q0-5jf6(olYS;3X zv`cxV+C@(LW?S#5_Gjl6kNO>?-Pz3=r%>(BZjrx3wQG4x+C^Tec9GM*+15L<{nN$& zvk<*g+lQon%3FG!l)MtVB)5GKyT4NW2;DFAWk;9)ng7erJLdQ`6*tQl9di6Y+M&Es z?YTVEys|u0d|MtWZY>WL-^<5#JH8?F%I)^dFYH_K_NI#-^4-|=Q$sB`D(w+?*%Z0cNk z`VHHfb)n9!T^{OO+xnr-y)6%QE^hr$=jN7&I#;)TsB?GQ6~0^Z=_7l5K5y#We)>ZP znsp(1AHLb=^Cs;g53vh*NdJ(BPuKfL>JRna!SYb=B`gmgt@oYihkCDJd8l*w__xFV z{@xewmDgUk)$fKcSTWBgzt6cA!*M|9>*i|3-5dX0sGqtb5KK3`}7mhX4AKTGv z3O{qEl!w2o{vZ#vPqAK@dVBUG#QWg#Jt_oimvd)U8&8&%KD^H&e4`ey!ckL!cfm*02wcP;hZ zGHB-iTjvJi#-BgZ%)ISvWy)c%{x6LcebM?ip02chEAQ3+nSDiHwEnZVS6aW7_v&A; zv)TBNqsAQ|OK1}*~ zO7Yy%_DWu<__19*@ksp4p1;k%hxmW?p(TzW^+ev)f5)HhweUOcw|M-%bFebyuvdTY z!J;o(e_tC1qV-#OpFYpq{0@>Qc)ku(KC1oMsZ&K?bpKZ!uC#tD?`wbXc#XH(hm6DE zC(3xhl=rny{LcR8(bB%iJed7g&A(7_iF{+IxV1b~Tw5Nh{n`J!*LFbL-`3w{|9-X4 zeGc*r#BXztJTs)?j(Z&BDN}K2d8oK`d8oLyJXHK!-jY0wys~YV_PgvOz6Kvv`|^j0 ze?Qs!8Kzw9(D(jl?m4GR{~+z(pm=S$aq(@<-1T=gm1FxY-x&Uo&NGpRkJNdl^f!j| zOZ76pP5O;I#4pIh9r7RY5WgZ1@&7MqyhHVO?w2($LfU=V=F&dYxX-|MK?4PnZ~j`8#R znZc*`xqXxI|NQnc&!Fszyib4bL4z{>qW!q|Y^CGAm3QegKW0y8e*e&^!p9tTKjy<9 z-%-lLC+qo0{hbe=d8Ww2=Nv2Y@O_JlJUps)Ul5B??#Om_Yjdo=VRSh3{yTzfAHYV)`zq^SgQDJiC*Mg{lVYdqucm#onMQa9w}^H-bHL&{`;9N zL;mp1&EV%&H=9EAb}cm4hnJph1~(oYa!T(rC=Y2L{gxZCOY+JGPFsIt_|35*59ycm z-9L!G@kh76{eFV`kv#91dd}f_fae|a3&)zl|2pI69aGOaE)VsbiQ zTgv!`iZjc@m)}v^4;44DugiZ&h~Jy8D)9@Y$NF7K9OKte?OGlZr@yhS#4D8DbH6nh zQvI?#RKM;2P=0ayq5L$5mT@SNqJrnN6B6|8$c{+|4|CtMy^ZVXyuZ8rwS~TK`EWE9nR2uvcHt z-`F`LT3^rSVaogB*YMQgwI2vmKB|2`m&rd6J)X}( z{1$mv`}^_#Hy510p!42?b`Gih#Q%d}y@SNTtmKvWm-6uLlSLjXKUp3sKUzQhxbBm* z?f2O?yKIr|GtuKYGE}`T4`~;9D7!2Vu^)L$`h~ob{IBOz`+?_F*=K#R%Ra{6`$B%F z{h|G?{a|Q)_KhR-eXgSMhqTN2NXWQH-qpUxkLP;TKO>?yb71q3jGLbGJ$^j*tKY~g z88^y9`OWf>aYG);@0N#cyQBK&b2#ePc^rP>+%0swoYP6((LX<5&W@ig>jPwc(D+rp z@v3Ha@;<-+HTB-u@=))UT^{PawB@1p5w1VfKEm?wA9ojd$htxMA?pryg{(V+kjc0((c2zHoPA<^g-XFnVHF{ky0i{o76z9?|^Wv(Rh`6{j=5a$d73q+RBHNV~7Rv9u3q7kQX^ z#jf`4U)C|j&kR&N&#<0_DHr?neV(uR44ns`wWX4I9(k|+$Hu)L&Wb!Y%^p8fNj{`} zZ>`P`pu%puXxRrOczaRf|PEKAUzR0I521AlhiQksaqmoyW=Qv*v?>=dL z)+6e19z{+*h2&An!?xYNe0qfW_^s1EFEMpaGW|a~FA5)3=EZ#Y2M;vU-?^ii4|NVQ z{oi*r^Wo7`MIL_ezGi0Wwq`zjyW&80&WAdOu{`|YJ4^fF52?P^zApdz{fy#~^B&@o za~Rf_O7&yrFLizy=67OOeU4Y&&tMP4?zf#CGUZ~gKJSZu;+pav2z}lQg((+%^*=FR z^dWkhFO}%G@-BU!FY*3N{bOGETuSq$Qhu0y`*Hij%LF_S8KEfaT zKTzvK^j^7bNc3BISAQ4}+Vy!PJhj;ER&pNM%KP+ZK6|X$aPwGWN`K~W_PHK2@;69&#>?JY>Eg51G%%L*{d9Uzh!i z7tcq|Pw;Km`gy97{L;#M^);WY57kfRQ6>7VyiXs0c|Jqtv-(rXd~W4^`aFMo{2=45 z=krR&ua$S{@1p;bGj1mKOW*cG)}^N(8uCqhi#*ggSsrSftRJRcv8#Q@yVeKCJ0#xU zeSApbobr%3M;_8&ttaj;B;MQly4v&n{LwST-%$FqPdqjx{%+-6`d&};d_p@sr;vAM zuUkE&@(#}fd4i1TaEAP|ieB9#?i6@Bk^p0 zT;`{5p)XMX%dvmsR6$G$H6i67n{(SK8R@qUGQX{KE4)z|w~{Lgz- z`oa6v%9M+}`mfV`xc1&=02P0Ow;UfbK&w%-0QjhK#xIUP2vCE$Q#M9dO5>Jpg(0zwW;y~|z91oB< zLLMqFTOKNJTOKOETOJZ8=!e8h+kTgQyRh$ug<>BhzJLDQkgq;nC$1~n0`isvNeVFo|c(wh1f6jSz4k};H{nNsb%ENR2deG~wsXT3Y zsCCxmq1IW;L*;AN+q2$c|KO&bCEg(Mdh_NXiLX}P*MHszdA|qA*I!XTLiSs&yib3i z_u}m5Oy=9eO5Qh9F81onKAtbIljjcX4^uws`D3v2beXS^{_j3IB=fbE_qMNm#kxqo zV*JQcl_?+Pf9@C1Pl&$m7gVOar~msIKj}GNK<4-IQ$vy`RG;$&B#$BwH7_j>H4iKg zHBT%LH7_j>iI2AZF8|oRM>md-0n|R+_sc@<(_J2FA8&a`|IrH-=az@>TU6vB{!_lS z|DfW}`Xip-i5u4si9f|pCGEEIj{cSV6MxyZ1L>F6ol5#ec}TyIht!Weq+iIx-PbpR zgI(_{+PCJfvUfhm05Y_KepN^6hI*S#HYTb8k9qf15goV_(C0oT+oT zxi{YK^Egv}n0wuw_J=8dxIC0U*bm_kll-gw4bMHM#?j@W=C#`mWq0iBh?mCsR{6yD zhD6^ZSH1~T4*T?{4sP>3K~wizrw-{nGt@n|$b0o~UhqA$DTvSLpJVsi&{M?1%Wf8l2CXf3~$*`M*_v zOU{c|erl`Fi(&npe&1z3{-0W^{?boK|5xlBGUYw}M}Ok)6c6e@M1SH-O=Zga`eXf9 zk9ob~@4s+=kH7m8vQAOnvtIfA+~iO0ar@?X&-w46aQ~CPi$cF9uiiDJ?nSyh)V)c| zL*2i$Jk+>QFZVx9jhp46#@FSc#^3T#_dYETZ(Q9>K2_rqs=xew7y4_`?sHH2{->$? zpOe3I$oD@@-TQQTh~4Oi^b7s)qP;~w)O@f!#6Re_#Lw7Qsd2^rk>mFlihssQB>pG< zU-_$L%3)Xh9U-1CzRmTU8n=}z_WS;>`JMBcm6vU9=38$3<*S>O6MLIV`E}((kNRGu zDZg4C%C9SbaA5zMvTNnKMYhkBA6(uNJKb)m{>Q#9`-xxvo(ldm761Ib66S%Kaj(Vhl&r&L;CxX>D8x(OnFcL$)D4|vfASht^XURht#~9e&IRiPgC=58hNOB>GGDu zY3l2>_u&WZ-)TsHuiP=D{O@wHPk-eDV`Y4x^rya0_JoXIEAP@Le@|Y#Q1X??ecp+? zw+^Yeom_da&E*jI^u$?Nmy&)B{x zk#?uPcxXtqJ9Urx9jaZ+L$zypOWL8HzV;Xo{@yL~!6Ys|e7M91OnJ|I*d_gy&pKJ= zt;o8t@_Dj1lwB)-Ww+zslwFpG<@rZs)=YzKXuJOcv_7n8a`wESx_Yo~qKC1tY zf4zT(ynknYguIuIysLfU$@{2}>+cL}KgE7#>Ir2(W9q#(`z79gd*Afc?UwWY8#(p6 zJiJ@uOL?gG-j;{jH(4IG?e@L@_W1s)=Gk9=qnQ%juNSxd6jKhn`e%Lindpnw*M2ih z`AB{CyXcG7(|$Khd7nP{XX=->dtYeMZ{oG3{jE3+@k4&!*B{nB?rWI3XT-Y4eIqmF zVxPXp`K1eGyrt*y4O1@m>GOBwxKCt$;h6tU8~0K8du_E`@?L$dkNzF}+zGFrmDJnH zd-aKP-4}AaV>j_0ro8(;k@v4tx5$3_33b2B;~GkD>dn`>e)BDBi#*i5ESHD6*Jb@s z_jFuuD8E=9{^RZ<55Kad$iuInEAmkHaHejRUqkvy{DrjphsS-t%sf8dOnvl#?*o~- z2jub)yU`En7y6;@m02%T99bUXAM`_wm+cGV*AemKb%FV$yv}_e@_p#}r1HM` z8{yxH-1p({K7=VBr9b`V&Av|rt@n;amA-%3%KPGv|0nL>iP-J`@7*%xqwXtt->Q4M zp1(hQbD6g-xxX8EU;9%(cdYbZbRK%{zDoDMmG|mjIw<)_wEh*EhiZRHbpC1Oz54&6 z^^SZldffhPOJ&Mom%jI(>{}&g|4QCt{~EHcOs|!HL#->`--dT*;Hcc=3K+ z@#egOpS4e?zuvdE<$dw8pZtxViEGF_e7^b}GEeb;$UH_KGS88R;Mz9zq_b z-Jbb$MD|Vp+%2}xl-~5SZ?eAm3Oy%ipT8;m#{JFob*ev<-P1pN$o8A+m*t`I%=E8~ zIp3J-7yEzuXDTnaJX9WVyP@okeO>Y3@zHvM|Gl0tF03OhQ{Fc|*4Oi@*Fz|KdCshq zT|Cbs?`og%UAeun|4jLh{UH7~H7+g>HEu2sHLjM2@{8r6{A+nAzp_6heoXnt@}BW^ z|9)+ISuY{|dx^#~l%H3&@~-~*`R7vk_v3q-NvM6tnAbguuI?m`P=bgr|9|lFUN+Ye_uRP`X{nZPD)-$yOf9c z4S9%Pk%#yndC0i5?RVMd`LkK;uj*eZ(!Z5k4pgSRuYc(K+*<8G^f|u{8K1~|+kfTh z68|FpS^3(dm5k5IR^F%Ya}({eAo`qlgzUc}@6+e+KJq*0@j0M*qyOM`w5eF$qOOvBM-^VIqiqo z-`dybU!SWHM>=2gxIVCbNX;M4+sF?-chmftkevBrDz04~YTmd!)V#4g)V#4gY}*}` z|NXu~`q247?*l^5OO=n>&oV#RkDBX{%GL&J0W`Bee7%{^Oo{a^Ov~i zeZQ&q^u#^)SIm@)UE{Nh_)xc_>;Mf<$xLXY=fl_?*kue^o5tcm1;NgF8%%F zPvtHAZ({!kH<$LI?pu)0Ds|7o^6>NXMIOqJmWT4Q^}{`y7t|Z3-JbEqKJqW`&&{La z8+2YBzHgE9HTMC?+uSp-Jk&h|mxsEiV0ozb-j;`Y|89AhcEv9L;eYn$+?O#wc%S#- zyl-beUdtu#(x-pye;xmj@%uuF15^HIKaBrP`Q7EA{OwQ1L)VR4kl%FQ|YkWi9 zOJLq}Kf%l#1DC+}a0Px!WL{XAQla`EGC!VK{AV&RwBC7sK;{MVka_aYw?|EA_O z>oxP+)VyE9rMD?`wbM-HreM zi>dyvy!WK_P5r-@$b0p9pUv-};)M6vl_?+b|7WKDbVsot;{U&z9}@dpd0+d?2mb$r zNt|lFI9_4O#a{iNRC|mwME|MsN0{;vuDF5;I(km6$yf0(_Z7S|94`r9jL(Ln@L(MD8L(MzOL+oqY@37DBU#I8g z5B<(WPxpeDzb5q}4`~;9h+W7->_#3cuTO8f)%o4jx?p*zb;ISM)*Z`3{6hU9{@c8# z_z!A4xW811-qnhaQ0vz8(@uK*GPQ159?~xQA$Fl3YTdG4sCCQokp7_`YM)^H!uVC} z@W20lHgWCz0>7p_b71d~+Mf~c?9WW{P5b+f`n(^Rx>)-B4zivozgKEq0&pZrise_DB;KKC7Y9y8fj z={}^_O_*}APoMYY-k(6tPwv4~Dvw0or_Xxmc!l&|>t!YVZ{@xET92#`(bsxaiGC~Z z()a#I^I-i4Hao7@e`JyOBjJWCHhcdO*5B#(UG+KtKmCmBn}XD*{b%LzwBxvz zp5we_%3+s2{^8z%e;LR_zqj8&zh0<$)iULyTmU089&Zl_uL&of1Ez?_q??Ry#kfZMo*Nr!24ht3{Tt2|ux|$iolsE%NZa3q>Bb?b3dqeJi&& z?w|P|#~RLI*R@=~d6Dl!R6b76ACj*RpLn3i!>1{~BM+Z7UgY5oXMC=^z9oK3`(5@i zemtM~`y%ku+Fxn@R9>g~-^%;+c|LMG;`;xk^+?Z8Vaof)kM>!w{5#S8)_NAEyicF; zM<4sm)8aR__t`Q;R)@hkcZ53^h4%3^6(esZ{%Ux6}$X%MD|Ty zcdz?r{_DMEzJ=e}UFLU7#+CV4sd>Qt73PC^gU-*Muk|g|xVt=j_F~WL^)1)^`-#$i zsJNSa-U-K_srZO}z4rgzV*7Uzs^63Meq+ezuWBa$&!J{*c+W1spIaNs@4UCe|E9)= z_ji1+##El2{5OqXsBxM6`tD|Js61+Us66WSL;2lyjqv}}nd@!8Df_4HzuNYjvYY)H z_M5Vs=TGc6WjD{C*l)^imxr=@>N}@xzbU)f4`RP5yImg2ZnqoC?%3Dmf6tF!+up2G zym0S{IN<(Em~z;qPyAsw>x-$pKJ_=sKcVtI`(4%n^T1eTx-E3k@`QU{%C%BUy0sx_Z59H<*>v5<@sOd5#%q<4aocK<0}70&$k!sZ`QS3|9yHs zX1^C+uIFdT*N0c=`5Sqt^@Dm@KTP?>@=)uB%R}WC%R}W2%flOF=R@V6Q2k}UiN8$R zU3Ip!57%z<{nW};dy728ZuCR?gjv;Xvae6{QM_AOK1w?B3KYJY@% z?2~Ah{ZW|mKKu6*|GM8y{6WTbYSZx{HSXMlULPthEDseomWPTf%R|MTS8@&%;dLAIYOvT^I zWtz94;&bJa1;-~`uee2Csklau_%;>ymWP@b)(bU%EDtrmtQTtDVVCng)cCEO*LZ~* zH=b7*KU3pod8l!7d8l!-Jk+>Z9@6gm2Ry$`?56&Zej#sZzerxmzE<`z{$~6tc8%XI z;#2QqydJ?P9rpQL<?-J`N8@}oQj?wUpzD< z^WvUkWqv^B1@e%2L3zl$KprwLkhk=_ki4>Ochvmg9>3#J^t$(vvdH`Nxi^3xP5GJk{`lKWx!9}EeByUd-(Mu2DpTI0&-hIK>EZHx z4A<}Ud+5qX>-m-P@bPDhJXAbc9x9G_kInZRO`f084|#6Z`)fZxL+wk_eqaCaALj=q z`#$;C`#+d+u}h!$_xR!m##!IZcH_e9d!qY|Gl5-;Dm8_G}_qqt%di&0&h)uApdaeF&-I3S zF0?#6p?IU+P~&fT$a4?!kmsHiikFb*o|Om7^ADumHJgWgkNp2bi<-6J+vR`cA$Fq| z(l6xU%MKO&@P#LfJj5^Phw-D>6|amJ=fmWC=za8y$0}0}`}BE^S*!L<^`GY%pSQu3 zi@o}~Z->3yx3k`BwTO#@q5xaW+N0q2kQ)P;q8?s5rAc;aT#TkD;hIlg-XD$!18~u=ep&u&FtQTq=Ef4Vz`l0-6`$EN??H>_;{QoZJ zRrqP0SH1Aukg6y0j`8#J0RNxS`VjpO$^H=iR^F%2^DOxddLORm*)Zj!o(I^E_`FhF zyYf8mSHhI{ohOp7`8z1A_mKI=-$SX?zJtH_z&^p$zQgiR`&E~>I}YMxGCHShC7Q}fvJQ1im&q2`6CC@RGS1N9hvwt-eSC)s0Kh_W8 z57v5n_M`jdKkl;<9}v4&PVXF2=k(lv<-8tpPLI4&=kzWQKe4UIL!HxG9=>;>$iueX zzW6*M`zBs|lb;7nJ;zMEROerz&QZBX&$%k(oOR;m+lPGCcr)>$eSTgrZ_xciyYz`K-d8ve z@!o>@@gCV9Dz5x~qa|^Lyi#$6ocBSd;>z++apm$*ab>g6}5;tpF;(z3o%Ci%%InV2cseVlyxZnLV)i0NaE2bQOYg?{)&t0Y6P<9h{Yr@zi zcG*vSaWB;V5$)IOWFP(&Qx5y|+2_-Lzc)~SeBR$O<*-BF`!C*)_;-+a)bmT_y*K;+ zQ>_W_InYc#;%u`f{PK<>4^QtZ`YqRf-z}C`evkIg(q9{DJSHEjeM9)*o2qFW{-f|6gsl1YYN$&nZ#V_?%%I}tkid)M=#g*lu z#@+IeacTYA;eUS*lE3%l=Q{WiwWq%WRr%pd%KFf9!>VhW$?ug(LDpoD|K(e?T0*Xw)S<|PyF)SZ~H~J^O+r$DIfK` z%DTn=1ftJ+6Q;ax|3dpbuTwv?o}Smkl=rp2OZxmjDE1$w_8ycIGTR# zPREnk{;tm-$w&Cx_tmLKdC2!$#FY1q$9~2`>y`a3qQ`ygO2sSd7WZQy=N!l@^&H^x zaN$6ahl*FrL-o)4A#u>Q-{)_i2NQ2PKeJsrKMUzsir0` zUv!QT@?IqJE`8reTe48bA2KdiZ>@Aa*A&0;|DhZw?eDwlvpt)Sm;4}lUhBLx^mw)M zUVZ)l1OHA$Z|eI_SEd~H>T{2RxDeg0?k|KXAF2OJ?MI1c(R!~vTAA{Z&$pjHQ{GpJ z_;2c-5^vD&t6F)V{hViDA9Vk9UJ#~yl>IzkV;{8Mp|&0|U=WPxz_BbkNErx@*V_vrQVacJkO?^FDQdapG5uNu!#@0~0Uu@k+J{-7V~dC>YTc`iX-i9e*zd7r80 zl-ZN}{Jdi7dBuJd`^KBU z<)QMH%R|M9<)QMs<>6OPG=tx}xmh26?MRWg+%Tr^h)Z61$sLw&2$ffE@Yi7XFWc<#H8qZW|C;fHteccq%KvUZl;3SvkN=Oze}mWG=X_-< zPYvFr`=FuDDTrszD;(F)UhMCyaBeYp%W@{r@DC3ygO zrRKle4>j*&U)T8W$NrtlPsEwXdg1S`gb&la*{$`bCHoTOmCReo$>%2X8hOaPM;?+N zkcVx%eftyJ_tD1Z8GO%$a}4HVrQRFzeHY#znmSjsJk+_O%R{}Fv^;$DpvXhL-?Ti` zIf>g9yZpBn|LJ=Wp09itf;_=@Ct9W)cIoeuKHn8W?(Yi`uXiZlgv9BIeI;I@@{Z*# z83*K*8VBTzhuPNK*FN^ojUV><0XY|&8|Dg zguG|Z@4M=YKRLg`uO4Ulvohs9-^wripNm<0Lw$IvzE8? zypnz;^Qx`C%YKjNYfia+zKej~+$&{&OU`vEuT;Cp*^ijA%kmKay-DNJl5>rpH3 z(mz5x{lonwo*@0WbNi6^abb7yr?_5u5P2p2l%D$w@!OZ~8d7=I@=$qr?&KDqA4BrA z+YgCj={uew@!Q(hWk3CAJ!HN?@_@f*)-vUz<|FI1^RY-C@b|P@ro1m-(*E?f6tBv6 zru@(Md-1=Sahw)@y7QQi91NVr2Q`Y+&}r({e$#NeyyZ`t-MR$EhGak2%De2r-!or3SL}h<^UZ}J zwa!`|Y8|#bR9spfY8|&ceA_~ihxF?^2TT7T{cGFrvd{hd>D`V;zAH(d;CquH@ksfo z{Kof*{X5Zd^2%+MDIcjX|MC2cAK7o=-!SEU&)2);-|3Ue56Um5o+J2v@A^>B87^9+FRxhnK6pw*4;q zjxb+7f5P!#uvLn*6+ys^u1m_ z`PQ;-YJ9z}g((+%^*=W5b4P#gT={19_!;LL$ay5?efQ@*zb@EW;!|WlpQyZpC{{38F7 zzaV*SM*gpS+^J?}{Q?q=vTJ&j>?bT@6mtl3vIXdb)oHNzc?y> z*tgP-=ygr|*D&Ry+Gl=xKMytkomWE5|H!-ix1anW|9T&apXI;VwTj13dClVxDt}oX zvhQV_LiyYBP=2<&rT5jUzmolL+kTgQp5I?qeq=u(vY+Dn)nUps zw0+0XL(UVDgl z)-}j}fOrj4F81lOKlO8_=s4B>G)y_{(szFjY%cwQJon+(mhPAQ8@k`^@4M<_eEF_9 z=TQ(nzHc6;9CqnDey>|?d-%@0{rL;$hSa&D%R`+z&LR(K7rjvDWtO+(T+`pL53%nB zWgH>>v;L^@QrvL=o%rFsKk-wUa2}?hz0-l_?i{^%W=FM<8C@uHvOK zS)&A^e-G2#F-lI>vFs}69JS}Q`x$kWLqWt|Qjqhih`CX4cc2S@9@!ThZ z+~Z+CUwQuSX7=|E`~HvlJ%@_CCHHVBuhcyp^tlgZw)IkfpFimb|1Srwxk~3a>Q9*R zQGZW?JjOkGlkrqO^LWCPi+%00PIx>a`dTk4H4c&Y=}&)4zt`_f>2vObzM1l#@pL?D zpW^o6N44L2^wuG1zm<2i@ArW-Hyv%bA7;LH!TXjqq3%7oJp9DAA`dlAmWLWo_C1WJ zsry$mFT2h6uT0&~a(PScZBbtN;F~Pxeiy&v-WT;(>R#B)&3k=6%v8Mb{6)N(=p9rX zhKfI)bGd(JD()-~X&?QT+#5q)`Q;tfTNB=MpvXh|Cw=ahnTk{FB3{k-RqXJ;-v_eK z_4q;Z(I09Y!{=)JT6vfL5$2!vGmM|9abv&F_?hpy%jYB9W8|EKdyTU{abigMclO8b zwSP_d*YZ$)o&DMEWj;gsk@J_3dzZ*7nXj}T#xAkT{{6Cl<|)TYK8DIaoKsYOXlId! z_yu{W|JPyt@Xq5!9-i7${UCa`p5o{{+a5R%R}|c<)Qjzd8mF_9;#oKhq90TCiaL{xI$K*tZM&-mu8?!+h2DX7*K^o3-H^Ru_3lJIF)q zKtH5^tWTllSL*Nb5AE~*gLpQT=LY)*UY|_mIhVKO-YeymnlH$iFQ(?xK<`(`SFqNr z`g_}7zqR~M0M;(P1lDM;ROyWx8m{Qi&p#(O~W zUhM0z-}jT|9;ES5e3_c3gKzHjyfwA1^Zt-^-h7$-hdk8()3RQu|Hox{`1aF9FMQ9@ zA`kWd#jGD{U7tH&@fa!&=f+Arnwn>Gixn@S=9$Yw%`?kG#kJ+3;@SOW*Td@8P^Zg&G&`uT*MW zcu&Xpn9OtJA@dx0sP=iEhrdnPZTTqsb+5(o0)EAO&@zvj!_V@?)*h~FM} zY)Hn1@{n;u9x|@TL&hC>NL(Nfi66ZWCZCzuhkR83UUSO(W!_7351IGXA^T*?N9~sx zSMDoA>uFrWl=t0dc0T#h6Si*vm464D=7(e+lIKG8+wxH3!TVOm!&JX{-%9^Y`Nieo zJ-bRi4E1-5+-|7x8T|MWkB=$8S>Elxl26{FeEiOH%^Xxdv>!t41Lt0^ydEmg&i&3= z=UY?x*z!>OP3yO0ABwzEc^p0Rxv6|_d8mCZ`s{bjDcSXbapzf6dDZhTUccI9`_)+YdPit9!bl-8!M{YFf zC-)q~l*2B4=HpKIw#h4 zNA!<-M8vyEzPUs5JxsaS)jyu+mu+0|d@?l;mhI8_hnfe=9(B<9!n{uM_V-=&;cw)8 zpA_=_AN0bMkNSS;ig)Qe=KX`l{G+qYiaXXe8^V-}UF|boD;_xH?_HbUJ=ct%x7*(r zhZ}!vtL2qX8n>M9&s)CzWRZtE4wdq7*Y+Y0Z(QAsFW%zsqeH%!^J-^* zUHaGJ-?L}@_m<7ORy8aBLhl#C&(Ak2KB@N(;XO@}ho=@dE6(XWJ=EVRPJLbN*+fId?^l~2ysw$~Mg9HkP=8N*;@1=};hXgT0g;E# zIacK1Gxh&Lk%y^Q>}uckzxYtGA7b}!?j92RTX~m0_OH0+u;&xxdt)oE-81B+8vpCJ z`uoD>l;#ccmV9@N@=ASw3pwB4GBrm-|WBBZ0hefkKeJ#f4|whd8~=N%YOU+uZ|c0L-hYz=h7kmZ{>aZ z6WjLq?=PGBp3B7ctqtFEF<)>~6M3Khia*wO8S%5o_l%hzVaiAKpZb}PkaJrj;b!?F&G+!fcNBT} zm2Njc=Ip9{Z0F-^)(BeN*E)F@4nIYd-4sX5t5T`g>&NA3V@Zth~eD zBQrH_6D#iW_ZUps@A8m!V%@f~UO?7~jhly5+*lsUPPZGX|FN&H{}bQ4z2q0DyfpEU z)kBhBT6wSj^-a--`i=neyAu6Y-lxB8`zhyF^K1K>WjhZ0dj_WRYvf(}%&%piR{Ut+ zV`{&(?6Zr!--7J3D6b@6P#$WZWqGK5mgS-LS(b<7pS!hRYFXP=`(5^VejU^JOTJv( z_=1Nw%a3oaOgZe+U-nIX5A*fPJ5b-jT=qcu9;TUcu}^>bk8bw&G)?I*f8rw7W4?5u ziM&JK?^l)|I#=|ezL&cEw$nqt@v3I|yYBb*rp>pkE%NZ=*A#j9$t#OI{EYG&^@d+i zexv^IA9ojd_?0b19)A5?k%!+_e-1DBd#on?J#w`47t-z>S_i}9%C{%99)`!1kCBJi zjb2E-bCul3Jy0Ocpvy-NAf z@=$hJ9uhC;g{fEUYv20@$xXHI{X?ks*?-f%sdg<7)vn7!wQG5pdPlU+a}oVF(R<*K z?Kf4g%R|~l9?CAuL)p!968-m2Z%TpKXdf{Ijh^9=7d{`rfYhMcUsvKC}<^__kzU7kStClAqYWxPK!1LiROb z%KO^KKkP@4L;SP+lI=svFYHI~55zCXE9DoLhw_W%q5NWbD8E=9w(a)VSMt~6wiWvz zddi2D)Ju6tyU0WALLOp2^6*>dHOn7=RkJbt_WecPa?{KumRHKZ&bJ|cQT_H0#D70N z7*gjK&eI`!&s4mH8t3KD+2iLQQ_nq?hqO<=Z@Ce>q+j{KY3pwczd2UqA^noR`v(J1X#pGDCvE`B#lU*A;9KPwj9WdE2v zekF?YH=kBW>d@=BhAXg`$Qv9HU1<_G6M{=LX^=88W!SebIzrO*6e zUx|Mpc8_0rbV&7&{U+lNi7VuliaVEwic8Bw_0RH9eziPo+wB=2&yVq+I$Z37=y5I+ zQZMBp?II7c3wenB$V0`?__kXeKc?c(@|MIU<&}zS9~z00p8{4P&)oq&5O-fC}gL{jL35_dk2x^Vj$DS=W8-^>(dmUF&7v zoAm+hW2!w^KX|`Hq+dy|RJomRm~u7B&-lQuiC=B!if?U?J(J#wZy6t67fIjsR9wC4 zNvj=Sn~J+B?^U7Va;Jxi+bs{-S3o~h{BL>BP2V_gd1aq(t9;v3zPV>;-L><+!e3DQ z^&G7?!W*|ZF4z>lYEO}e(G!RIf0^h1&^_MIGheEBPVoWX`!ipEzC1s?6||#U*w_o4f6b<{OLc|C0@Tntv6YpRB9a=c~k#y_WFC6^HAlQe_-o``VOYk zr+g37al@}(SK^2B_m+4eOuE?0pZ#0D$7w2m_ID|Vne-|7&wXD0O7;$YPVj}|Pv*p= zxBUB2`tO<7mGvi-ea$^v-xm)5?Sj&OJ+FGzHrq!f>q^-d_HDARlzn-92{n%9cAl6} zcJFvHe9W;T@9F(4@@HQQ%Dz*+R{OE8^zTLI_wd=uq{EiJ<00j5s{D=@!la8s`n2Et zL&{&}G}UhNU+&y~kaauhl^WMh4>j&B57jP~hiX^LL(26ps$Zz~o&VY?*B4Sxg)BO8G@U_j9r*o)5|QQ|iBva*-ZVZsZ~LLLO3o)8>2OGY8|DA-zV6DmA1I{!TAy$}XHstDq4ve+KDyoe<7V{4 zf#*B^S+z>>*Uz5pOsxZ%hgol$@;j>@y~XQy^VaR%;y3nq{b_35>GV+h0@e#Zta%&# z@c6ExA8Oyh@=*H{)(__xc@n`rY&1Z@izB_ziOOc1#H?cg_I@tQ5*2$KK zI`&6Q%FXjb>V-U{eUOK5I#iw?rd`E>_V@nAg3c4^e|VMr1p5V*+P8Olc-4s_ z51)Ln$U}{1&Li<-Fwbq-m+kY)W6p0L;t#%O--P(DUfwMK?oA~h(dFK?YeLFRdPupE zhm;$6NV)fPrTn7H-#@>Vzx}1=Hzm4VZuk0!ePq@_oHMens#Kg| zKN#k@Q|3?F$MFL^cG~S%N&L{u2g>jJD8$EnFWJ=oIdMPdBjz(TUXX`67jb&1_{Q>3 zag*iYp6k87;CsTXGx!d%<)OY)?DSCWWqGLmc|Si?{fP6apULlDp?(V0uB;PiUsLUC zc}PC!hm-^TQ1P7gLd9{Ghtvc8Q0>b)!{-+;?I;ekcNuSgC4Keh0#rXQ{P~>|Vh@bR z5IbQUg{tSmr__!i_V#z1ioL-+w_z{LN36GP*X#>up7eS=WE_mQl<@!=C#=^)#u4cu z{sMW=JhwI8k20SqPQ%}u@>9e~>}#8Eyrb;%g_ucZaGs{)qaUsyF_M`kSi1(?ivN;pYy!{-)}`@W%bFzo~jV zJygA&Z>V~wd_(0wpz?DbN&lJ3cm76|FI2fWx1@Zg^0z$HJm~aL^PuIS=0VFtwU6at z@}1H@oafU&ru;eQ{md(7(i{Cl|FRBod;^JVzV?*~iFH>HT%6e!*W=s$VP*Wmk*3e}sLR zvODJ+s@)fNEZaU!wR_4pRDSBueGL48NxR>*qO?Cuy4cd^yc7Qc={L?p!=$(T%T2Vu z<~zOzZ|Yu>`yu?~rf$AF<@-tIUv@V6LK2lsSL`t7N^OaDRjzth9yBev=@e`~1^ z^t$5hst@Z6k$Obl()a!+{*`^ummKx}XJyhG?RlTtV_|-&yK>L|!o54Z-+N_v^Gf@3 zzJrcm$4=P~uKdW6q8Dm?VtJ_c^z%ctqvhe>9WU}w=gaH|Um5DWoBd+Wy-l^N^}{!x zDd}PAAr7?{_tO|3)YnwG7oWPiWBp{R+)fWwZl{MTH{;;SFwbe^cR=~EZ$^Hm>b2@g z$}d#C+5fsS)N?Hlm9OQY%4K<|a$6p%-z*Q4Z=-z6^#6{7UVoaj>x*u;9h>ww@=*CQ zA8>!bRKCQ^w4eE|-JWk)e=_f|uFdm@`ePgUYd&NBXp;Z(V#yzpFY-|9N9HN+SDDcp zUO)PM0rc6wfR1ORA0{0Rl@EW#z7_jR(B~N1zY3E+ZU1TE^IJ>)P~+KgT~GWL@=D^Y zFD#bwLG4Qrzg6x!T;!qlB`go+H>@9OU&8X9>`Nf8q+R;*wdz0fw!_-1|!tts3qdH6)-*XQ4ApV@y^T(9+q`Nl7Hv)}BxO<~f-mj2vDN6UURWWRcy>KjTg z@|OPWGk4prA^S$N&)r;!y+=Nzk3XJ$gZza2lPSM9`%4<{q5R(LEoDA7<##L(<#(JO z<~dXR-O6t&?)ll1ovCv7ff5y}3?^pOd&D1#>-~ zruBMq&W5~F=WO^H&Lbh`c-9wN<-ZLj?UWZgA-@brX=OWhx#_6;l#S01qbRpArwEb{QG6GcDN{)F|z@43C` zhnjCG7xS&D`F8xRnqNcBx8p~t|3l5QmWP^WEe|yxS{`biwLDb5;|s6pt_oFd%R|-6 z@=)z#c~9nPo?of?HtjoPzegEg*F4_sZ_@sk?Jw;wvc4LvJ3OK4g`D#aQ|&T(%97j1 zRJ)9xe%|e4s$HBO>bz{UQSm^}O*%(&zM;xL+H!%*Z>sz$-%$BCsQlw+A98si@y7W1 zyDOPbB5&$%*8IiqMCPOM(`3&*lMb8u_iiuxBI~H}eXA?2-^+*e?cdaX%xAO{asKF8 zCnr>#PdgIto0{h=4?nr7q=%a4EDt|&q{u_X`Id*tw=w@wzVXk?p0Fd)cK_B>6Jqyo z>5BbB>>hcD-IE^DKgdJu9(hmOt>l$`zSH9E(G4ejU)iMIH);J5>b`R1L+x+-dG(Rf zf1=0v&&cmE-bMFcFK_9O-l2Gde4y5yqjw*kFzMou{<7M4=1J$B4`jT}Y}9xP<=31Z z%5Pa7%5Pa7s@yZzo^<&k<lkOo62`)ujYeL`OfUy1OJS4lsuZXtC zUf$B5z37yG2eISX*~69SN8ZvOkF`Ikb+t*kH|!|ohe;QQ^s(pJCn}y$TxovoWHQ=r#5SsNeWUR(qeq)V{^|Rq{Wf{OtHgXT85+$`6fiEdIzO|GoO&NJzfOL+yX$ z`9t-?zGgmhtc+`s{+;=geU%>Xy?j95@z(6?7H!9-=3CZL_*+Q4HM{Sk2^D8JJyaZG zd8jzW@=$)9=i<*z`FYDj%>zykHBVR`YCf?%)cBiy(^=0qCiZ+wZ`Y7~-@a!;`N7%W z+~V^lQ-0F&kaD9JQZMvF`C03Q^23&gv=90{J)Ts)O3hc4zcpVSp#NShyVHDYYQCS@ zE&B;I&(Blzmwq%KoeuYM!?|Oumiz9{Y0qjo+3Z!|&s_DwE#u z=dn-rf3Z{cy|K^Aq>BUg>F>EP53yb_wT@su@p=#H-X!bYkb9G(=O6I>NmJ{Y(Q|d5 zA!HqgJbaSY&;NO~_v6e<&ULJ(xjzY6$B9>E{J0bn1 z{V3jNHt9FyA^nCtq`&&-4dq9B<7YXaFxB4pQ`*~1`jqp8@d?EV#95-}nUjYqlispt z=l?_JO8$_1U#fbCmsx`(~#0(MJ1a@8L6dmGn^iaF&O}Ip~MPLFk9?TT$d8an(WjhcNkyL*=7= zy{?h{LGDpAUxn-=N8Z%eeC6MX*!lS7=PHxl*pK#o4(22FY0O`}t{XphpYI2mx5_^} zQ}cVMdE4pX+cp%v@WU@C@=*7ttRL#$l=Va1TXMdk_K%pq*?%(?SBzhw^TzN?=S%&< z2euS>sB>c1KU6$o{ZMg;;@ z%;T~X{AXp-8~e}L%V^!8ty=^^d>*>k16;d8Dj&k5ys z<{o{j+Y}}rai~7{H`WtwPslj=*S!-mP98eq`xPeR1bN6fK^`(rkcW&D*WLb_D?h4Df?1Brt+P+c#rcpH4bN1>wTk8gDH#s<-8#+Qsrv?PhtX_O(1z`_HV~;r2IW-!qqOv3;B5`{XnBH>UjH%+rtg zeO6O`(&-`PMn9xp=!f#d)(hpQEe~lI^ux5H*lKU$TiV-Hd(%Jkx2fl_KHzz#%Hi}- z<#c+e`dS{Uo&DTU?QD6dcD6iJJ6j&Aoh=X5-mDwE{)B38?5mP|760SkO|>`iKkpBl zYGw0L>K{QnQ8+F@qR^*-M<)ecS%nU6F+JugAk-}`i-`qA=G{?GY_Dc6+p zkKTE=$ET_Jc64Bq=VMcLI8uB{yk=_LI6c((U>}q5VQT!apUL&K4?P)=T7Xs$8s>xG!kp zZ;^-i+kaDgg!qj|sNA92#qvm(;9oUl?dgg_jC%p1VH}kyhUVlT*5s_Ew9MS3Fr%x4msP(tyq1OM_5Bq${f2e$} zpW-{0Ph>qw{1+yDnm%za@f{@YBkm28-irUo-}f3+KFB(a`wb!aM&2s_?B5=7`Jwau zhrN~7@8v`K>}Susf2sH{h~N3(`3do3+HbHwgHM#a|NYSOQXhWjdl~ZE)Cd1cy^+Sytr-z@^{KxY{jT_5D zjT_5D_F?ZjQ}$!w%`1yM)VQ*Kn0kmq^>usw>!#9wqWfLs8P10*X*cb=;D1co4SA@0uGSBK{(>&gA1eO= z?2q*oeuQzz`N`)`PpExe#v|t}=B~p<9`2AoKpsA4vB*R1D_h=^eQTay+2=cLzl!yU z`x#C+m?!TdLsz10NM}L?}pQ2Cs@IPKx zu^*)JV_z`o)BF?T-T6cE{owft$-kGk%8wuLej}7VexOqNk+<}jH*6owBiP6DG*9$Q zdaM7I)&9gK)X(vW<{!r;q5Spe{d?>$&Bw1U@}A5?q*rPlLZACBX8+u&?WgzCVi?t^Dg=#{+;4dGrO$d6x!1L_E3X%OCViSd^C|5My`Qi7Fid)@{qb+CZ+w0M z@oU$foKW*#L;i=G^P@27t^ea@e9yjhzrQ~P`R>&0J9kVtf2^B*&r-+t zM@@Z~)bgHu$DH)ad$(H7_nY_~->u5?TjihKd4G4sk8bMBAMd)^8}=k+V?1M!fK27$IJlw4Dq5Cm> zuNd;3HRP3jzOC{xK4+h`)8#XzKl_~PtZzPax6ga{4mRgLd=HcPp;F~$exUrO>SuYV z_L_akX}6cDdU2ja{UG1nBfV1J#dE%)%AfKz`{%J|{k^>z5&N2%KVHdq_(*SkkI(zN z+Rx>?_UzkkT(i&nxs@BXm-JBIgJU0<@4=a>2m80w$5cN#J$%m1-v8a$Gx@+)eYnTH zabEjckbKE6OuE?8=e{=OH>JmYZuHHhiv#-Jf93uU-0KRgV-zZ;emZzoR|!ALIkc_jh+x^4&o1uk@_H z)9;(@{e^R-y+!67?uCbL?_S>0=UyqlgVbaEG_`lnq__Hu{^s5#^@Y}ZOIMlnR(n`q zae#j(TA%eX`HM-Ps;_$i{+(#OlZPsk-qNQ(xbI0jo9YkF*XS2We{jAQ*56I(56`Rk zY0a;chxs+69Fe#BgZRb2BfdeO_@-ylr`2cX+vHzWK2!BzJwtsU^s7US8TP%+;7o*OBb z^eP|H`cC>A!w0nfLmpBu>AQX~?bxta{0a9*iQnPn|LKVDmxf7i#rNp5E@HkjHSe-M zV*WLgE)Mi}xgYl2JN$hS?s@S&Bkp%q>N`5b7aKx-XNUV+8$x}5gnMOtugE;3c?@~T zxYK;>@dxkGe5U!jl5s_P$oL}v@MU^lU%poP9rvnzP1&pC-|)Z9mbkg+m20%ms`$E6 zc0_!OJ(;p6%R|`{@iF#f%6@zwtLH|I1IN`N>mlOvFy(6W|55ed++&XWJ2K{b^?w=T z{r-N9sqdoUFZfOx8Bzf)Xmy-;zl(?i9@mWPU)Ee{n}TOKM-OnsX5U0M2T7OI^* zZbSKB?uYX|KFD{SkXNc-W*7JS`$4Aq&A$uPFSC!={M3{0`1yIE<^%MY7fkta%R_zF z$myZ_*YZ&IWqGLnbv?wP`rc0e{>=&dF_Uqn^AhF*6T3$qY94TUsCmHhko^SodwSeS zzmjqHz(M8jn8_f12_Pm6!1}UbEl!XsTbiZ_oP4+;D^A#w)^i-qnpSyS}?3 zl--Tj-PBzX%04X*_1*9BQ_uVR-=^%6_!IjyWmirQH4dC_DEmtJhRVOJ@)J+lpF`O> z@26C1JXs!U99bT!9F~VFFYy@PD>qe+Jbx&E<`edr?0=#BIQvbN_~l+cqPP+gex9lN(Js`_ zOuE?8XCIJp5BW|RaetU}IHZ4+_SHI!?}nNB9qU*$L^Wm&7?QJ z|7d;PyJf!To~`wC|F$ye;!yip|Lw<%J|y4Y+&>}uy}YH*x_~$wvaVoV5GK8~ULk+t zA^%RqFA>j#NuR2(^)vfzUPoX56|bM!&tx5)bl5EaN9Av{e>D=V_pxJ@NuRdA<$X}< zFZ*)6Wf!5#uk>l<=RAx4Gu8f_chL`K(x>zvahm6Ec;ivWOO?#)y?m(tJJtTP&tF}} zKfFfq-D@QRQAhE{7C=2q5N!bjC1T7s-7P2VbZ7AJLk6c z=MX=v^VmxKaW8M>&pMd>KvRB{^)UX_Ou9Ivzsz{m`2Rq0=yPwI_BYk;+~2mn!K4qj ze|aBX_v`Vu+=I7&mfxw=cQ2W*`TnJ;IBR^h;^9znoYO;nKNG*j_cQIk9(kMPd|#8_ z5$B;_DgW*Ckof|?7s{VgF5+`jeti6c+wIRy`ElM)Aig)zd*RNa56KsKPnS#SmDEde z*AFUgVc|7klEIoIKSW0>@oeNw;CC)Iy1(>fLY z#G>zWRI;x`diWvDH=j{|g?FDT^6(z5kCBI`_ZE4`y85p-m-RJFzG7?qaIb;infR@T zjubxzlRiyf^Qp%R^D1`CeA_eWu$BMJt8eyrGBu9aA7OoDYMe#h)Ytxx{U?;aLa!44 z*~^FYm+7CUXgm{NLD~7}yLU{e@!<5Hj9cWDswZ;CUywLU`}JXc?zH-Ub*tlP;!^w; z@oVLOzQ*w?@%B*pZJ+WNwnK5_wfB2GhDmSPr}fou%;&^K%=7eLnDlA${YdY(@O_I`XwzU4v<&UF3eY(swV|l3c!R#|`@cO{iy2kRJtSd;byznN=S-Q#aFX0KjQexWWOAFNdEZCo~&z-S1OJ~&-?9Aaxov9?^1dD z_8sbf```CpQ1&As`;^CzO?bocj{kwXBD`sTH+y1VcSWdu+S!lZP|iOf`y0qZ_Bs3K z59QDJCoZv_5T~$ie$>{=q&Mux|7XuWC;N%s?^OGHpR?!XACf=ZeWI&m-q-&JdH#p! zArF6F{_xt9-Q{8bT;xOLKfwH{xRdfhum9e+V?z0D;!&^vAnQKlA?rTUL)K@=L(LnO z_w>3?^2$EnY3;-PMf|bvF{*uiuQ60UzW*31AIrl>Z7K4e_?|qNs zkrkR=ez%&dwbzc8h^BZ$oM;Y zzKlP}xI-RBPaJwa<8R>wM|_{pRGh&7E_0vHRGbibQ~$NceV>s3KepZ*WzXnC-6M4R zl>3B>pWEv7uc>{_#lKzj{-&w*Z{$PyGyWES{k+#-rq)ILPdV!&Q|l(Dhgw%TJ=D6( z@=)tD%X_kJLtc5==5FD)4>>-Cth=l~W&OqXG+5t2?s@P%jWFr0_0O{E$Nz;Bzxsc} zwQlnNgNKh?^7l!u?78vlJ1nnMJ@|eJ>rGSjM4$V3X8+tqeC+w1_ILgw>o@u(O!_qa zh215-hm_O*+uakr$cOsZ_K98bJJI^Gt1#(P^FMdCw7=+f*Z*sWZvS51%71a;e5t>P z9^W?%$v5&LecR_pS9pF{fbuWcLCCs*^h)_9+e;|FWqBySW_c+8XL%_9ihbcHA%4sG zibLh2eHSm+e69U$Q~T$OPn5q7lP)&(e`%?-kH|W0@iw(nPq$AmZ|U>BEB3!k<;(Y^ z$lpx5*wSBox%>rw0^&bjd3M62Pt!kq%;R`bbiW?iSIPJ$y|uqW|MHy(iOJ zQ2vAON8mpo;}ChJ#-Y zIz7}mm*t_(yDSfNZf<#~ayj2d`IZ?E|M8&XCAe|r`EK$1PfWOY((;X=#;@g}#;x^2 z#aVp+gt!(GC*}Dqd&bW(9!=U?{?6?Ulin!*P4t)MKjJCRV|p*Z^KXbB`RTKchfMqk z{x#Hl2cFkL=J5|G?h1(qnD=@npH}>4`;&iVe}L~B@LmJo2l0#ehY&x!__J!)u>V~v-lIN?|Mjrj7rI|xuX549BJB|QKz)4wbn)1B z`(=}S@7!GSht%_f*Hy9)gS=AvG|1VnF}2TQd59hA`OGIKc8Gqc{&#w)ez!c-JYsoB zd)>97v==0wj~|&(cCq-Wy|xcic42u)InfKL5Bj0j71rzN_LIDl_K?2Y1u}k)ubz

    N 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

    q*bg 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%0Dd+u@Cwo_NIJc>^frp=HF6io?&XC> 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­)w7c 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%0DaTvB%S^xUz2=Z}2jok=znFT|bzc8W#V`JU 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 JULUH(4Lz7wp6Maf$e_dEdt3Z_)MN-&DFET75@< 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

      8@1;L}vHXXAi_|-QiJnt2&OlB?kB_^ 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

      -See Source#input for a description of that function. +See Source#input for a description of that function. This method will fetch +polygons and labels. See polygons and labels for more specific versions of +this method.

      "is_deep?" - Returns true, if in deep mode

      @@ -205,6 +225,15 @@ See Source#input for a description
      • is_tiled?
      +

      "l2n_data" - Gets the internal LayoutToNetlist object for the default Netter

      + +

      Usage:

      +
        +
      • l2n_data
      • +
      +

      +See Netter#l2n_data for a description of that function +

      "labels" - Gets the labels (text) from an original layer

      Usage:

      @@ -281,6 +310,24 @@ verbose mode is enabled. After using that method, the log output is sent to the given file instead of the logger window or the terminal.

      +

      "make_layer" - Creates an empty polygon layer based on the hierarchical scheme selected

      + +

      Usage:

      +
        +
      • make_layer
      • +
      +

      +The intention of this method is to provide an empty polygon layer based on the +hierarchical scheme selected. This will create a new layer with the hierarchy +of the current layout in deep mode and a flat layer in flat mode. +This method is similar to polygon_layer, but the latter does not create +a hierarchical layer. Hence the layer created by make_layer is suitable +for use in device extraction for example, while the one +delivered by polygon_layer is not. +

      +On the other hand, a layer created by the make_layer method is not intended to be +filled with Layer#insert. +

      "netter" - Creates a new netter object

      Usage:

      @@ -363,6 +410,17 @@ This function creates a polygon object. The arguments are the same than for the

      The intention of that method is to create an empty layer which can be filled with polygon-like objects using Layer#insert. +A similar method which creates a hierarchical layer in deep mode is +make_layer. This other layer is better suited for use with device extraction. +

      +

      "polygons" - Fetches the polygons (or shapes that can be converted to polygons) from the specified input from the default source

      + +

      Usage:

      +
        +
      • polygons(args)
      • +
      +

      +See Source#polygons for a description of that function.

      "report" - Specifies a report database for output

      diff --git a/src/lay/lay/doc/about/drc_ref_netter.xml b/src/lay/lay/doc/about/drc_ref_netter.xml index cd82c10aa..334ca9c51 100644 --- a/src/lay/lay/doc/about/drc_ref_netter.xml +++ b/src/lay/lay/doc/about/drc_ref_netter.xml @@ -137,7 +137,7 @@ errors = antenna_check(gate, metal1, 50.0, [ diode, 10.0 ]) Multiple diode specifications are allowed. Just add them to the antenna_check call.

      -The error shapes produced by the antenna check are a copy +The error shapes produced by the antenna check are copies of the metal shapes on the metal layers of each network violating the antenna rule.

      @@ -171,4 +171,63 @@ joins. Connections are accumulated. The connections defined so far can be cleared with clear_connections.

      +

      "connect_global" - Connects a layer with a global net

      + +

      Usage:

      +
        +
      • connect_global(l, name)
      • +
      +

      +Connects the shapes from the given layer l to a global net with the given name. +Global nets are common to all cells. Global nets automatically connect to parent +cells throughs implied pins. An example is the substrate (bulk) net which connects +to shapes belonging to tie-down diodes. +

      +

      "extract_devices" - Extracts devices based on the given extractor class, name and device layer selection

      + +

      Usage:

      +
        +
      • extract_devices(extractor, layer_hash)
      • +
      +

      +Runs the device extraction for given device extractor class. +

      +The device extractor is either an instance of one of the predefined extractor +classes (e.g. DeviceExtractorMOS4Transistor) or a custom class. It provides the +algorithms for deriving the device parameters from the device geometry. It needs +several device recognition layers which are passed in the layer hash. +

      +Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs it's own instance +of device extractor. The device extractor beside the algorithm and specific +extraction settings defines the name of the device to be built. +

      +The layer hash is a map of device type specific functional names (key) and +polygon layers (value). Here is an example: +

      +

      +deep
      +
      +nwell   = input(1, 0)
      +active  = input(2, 0)
      +poly    = input(3, 0)
      +bulk    = make_layer   # renders an empty layer used for putting the terminals on
      +
      +nactive = active - nwell      # active area of NMOS
      +nsd     = nactive - poly      # source/drain area
      +gate    = nactive & poly  # gate area
      +
      +mos4_ex = DeviceExtractorMOS4Transistor::new("NMOS4")
      +extract_devices(mos4_ex, { :SD => nsd, :G => gate, :P => poly, :W => bulk })
      +
      +

      +

      "l2n_data" - Gets the internal LayoutToNetlist object

      + +

      Usage:

      +
        +
      • l2n_data
      • +
      +

      +The LayoutToNetlist object provides access to the internal details of +the netter object. +

      diff --git a/src/lay/lay/doc/about/drc_ref_source.xml b/src/lay/lay/doc/about/drc_ref_source.xml index 360e61ec2..81a640cff 100644 --- a/src/lay/lay/doc/about/drc_ref_source.xml +++ b/src/lay/lay/doc/about/drc_ref_source.xml @@ -94,6 +94,19 @@ Some filter expressions are:
    1. METAL (17/0) : A layer named "METAL" or layer 17, datatype 0 (for GDS, which does not have names)
    2. +

      +Layers created with "input" contain both texts and polygons. There is a subtle +difference between flat and deep mode: in flat mode, texts are not visible in polygon +operations. In deep mode, texts appear as small 2x2 DBU rectangles. In flat mode, +some operations such as clipping are not fully supported for texts. Also, texts will +vanish in most polygon operations such as booleans etc. +

      +Texts can later be selected on the layer returned by "input" with the Layer#texts method. +

      +If you don't want to see texts, use polygons to create an input layer with polygon data +only. If you only want to see texts, use labels to create an input layer with texts only. +

      +Use the global version of "input" without a source object to address the default source.

      "labels" - Gets the labels (texts) from an input layer

      @@ -106,21 +119,11 @@ not have names)

      Creates a layer with the labels from the given layer of the source. -The layer can be specified by layer and optionally datatype, by a LayerInfo -object or by a sequence of filters. -Filters are expressions describing ranges -of layers and/or datatype numbers or layer names. Multiple filters -can be given and all layers matching at least one of these filter -expressions are joined to render the label collection. See "input" for -more details about the input layer specification.

      -Label layers currently can only be passed to an output layer. -Processing of labels is not supported. See "texts" for a way to filter -texts and use the text locations in geometrical operations. +This method is identical to input, but takes only texts from the given input +layer.

      -

      -labels(1, 0).output(100, 0)
      -
      +Use the global version of "label" without a source object to address the default source.

      "layers" - Gets the layers the source contains

      @@ -152,6 +155,15 @@ layers.each { |l| (input(l) & clip_box).output(l) }
      • layout
      +

      "make_layer" - Creates an empty polygon layer based on the hierarchy of the layout

      + +

      Usage:

      +
        +
      • make_layer
      • +
      +

      +This method delivers a new empty original layer. +

      "overlapping" - Specifies input selected from a region in overlapping mode

      Usage:

      @@ -168,6 +180,24 @@ the specified rectangle.
      touching is a similar method which delivers shapes touching the search region with their bounding box (without the requirement to overlap)

      +

      "polygons" - Gets the polygon shapes (or shapes that can be converted polygons) from an input layer

      + +

      Usage:

      +
        +
      • source.polygons(layer)
      • +
      • source.polygons(layer, datatype)
      • +
      • source.polygons(layer_into)
      • +
      • source.polygons(filter, ...)
      • +
      +

      +Creates a layer with the polygon shapes from the given layer of the source. +With "polygon shapes" we mean all kind of shapes that can be converted to polygons. +Those are boxes, paths and real polygons. +

      +This method is identical to input with respect to the options supported. +

      +Use the global version of "polygons" without a source object to address the default source. +

      "select" - Adds cell name expressions to the cell filters

      Usage:

      diff --git a/src/lay/lay/doc/images/drc_and1.png b/src/lay/lay/doc/images/drc_and1.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_and2.png b/src/lay/lay/doc/images/drc_and2.png index 1795d38c366af3acbf502c3fdd4ab01d96a3c842..57f0e81fdf8fa0cc09473c855a31b5cf046cf9b1 100644 GIT binary patch delta 23 fcmcaD{z`m;3J-I!lV=DA2gmJ){tq@P?&1ajW4#EY delta 10 RcmaDQep`Hk%0}bu+yEM21Wo_| diff --git a/src/lay/lay/doc/images/drc_and3.png b/src/lay/lay/doc/images/drc_and3.png index 1795d38c366af3acbf502c3fdd4ab01d96a3c842..57f0e81fdf8fa0cc09473c855a31b5cf046cf9b1 100644 GIT binary patch delta 23 fcmcaD{z`m;3J-I!lV=DA2gmJ){tq@P?&1ajW4#EY delta 10 RcmaDQep`Hk%0}bu+yEM21Wo_| diff --git a/src/lay/lay/doc/images/drc_centers1.png b/src/lay/lay/doc/images/drc_centers1.png index f08a8d4d6f1d4105f2fbf266dff29a23adbe482a..59d0ff14cc9a6261955f4e4b7e209d71bbd11b86 100644 GIT binary patch delta 23 fcmcaB{ziO)3J-I!lV=DA2gmJ){tq@P?%@UiWElvl delta 10 RcmaDOeph^g%0}az+yEMg1XKV3 diff --git a/src/lay/lay/doc/images/drc_centers2.png b/src/lay/lay/doc/images/drc_centers2.png index f08a8d4d6f1d4105f2fbf266dff29a23adbe482a..59d0ff14cc9a6261955f4e4b7e209d71bbd11b86 100644 GIT binary patch delta 23 fcmcaB{ziO)3J-I!lV=DA2gmJ){tq@P?%@UiWElvl delta 10 RcmaDOeph^g%0}az+yEMg1XKV3 diff --git a/src/lay/lay/doc/images/drc_corners1.png b/src/lay/lay/doc/images/drc_corners1.png index 71f72a4353ca0cab989c8b4c89d95d5778b26fb8..f94788dcdea58135d96896def51a4637992bbfbe 100644 GIT binary patch delta 23 fcmeB{m?bemg@?J=$uoq5gX8u?{|6ftUvUEfR)+{R delta 10 RcmbOw(Je7SWux(PZU7Z91Ktu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_corners3.png b/src/lay/lay/doc/images/drc_corners3.png index 71f72a4353ca0cab989c8b4c89d95d5778b26fb8..f94788dcdea58135d96896def51a4637992bbfbe 100644 GIT binary patch delta 23 fcmeB{m?bemg@?J=$uoq5gX8u?{|6ftUvUEfR)+{R delta 10 RcmbOw(Je7SWux(PZU7Z91Ktu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_enc2.png b/src/lay/lay/doc/images/drc_enc2.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_end_segments1.png b/src/lay/lay/doc/images/drc_end_segments1.png index f08a8d4d6f1d4105f2fbf266dff29a23adbe482a..59d0ff14cc9a6261955f4e4b7e209d71bbd11b86 100644 GIT binary patch delta 23 fcmcaB{ziO)3J-I!lV=DA2gmJ){tq@P?%@UiWElvl delta 10 RcmaDOeph^g%0}az+yEMg1XKV3 diff --git a/src/lay/lay/doc/images/drc_end_segments2.png b/src/lay/lay/doc/images/drc_end_segments2.png index f08a8d4d6f1d4105f2fbf266dff29a23adbe482a..59d0ff14cc9a6261955f4e4b7e209d71bbd11b86 100644 GIT binary patch delta 23 fcmcaB{ziO)3J-I!lV=DA2gmJ){tq@P?%@UiWElvl delta 10 RcmaDOeph^g%0}az+yEMg1XKV3 diff --git a/src/lay/lay/doc/images/drc_extended1.png b/src/lay/lay/doc/images/drc_extended1.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extended2.png b/src/lay/lay/doc/images/drc_extended2.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extended3.png b/src/lay/lay/doc/images/drc_extended3.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extended4.png b/src/lay/lay/doc/images/drc_extended4.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extent_refs1.png b/src/lay/lay/doc/images/drc_extent_refs1.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extent_refs10.png b/src/lay/lay/doc/images/drc_extent_refs10.png index 9ebfe37a34f85356c02ea36dafae6fabca3bc7ba..dfd44ddff0fa8a9ca692125734994efc1071078a 100644 GIT binary patch delta 23 fcmX>gd0ujY3J-I!lV=DA2gmJ){tq@P&gTIDUpNR? delta 10 RcmX>vc|dZ4%0}ZkJOCJF1Ofm6 diff --git a/src/lay/lay/doc/images/drc_extent_refs11.png b/src/lay/lay/doc/images/drc_extent_refs11.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extent_refs12.png b/src/lay/lay/doc/images/drc_extent_refs12.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extent_refs13.png b/src/lay/lay/doc/images/drc_extent_refs13.png index 567c78fe52d5145e92937c8a97b2633f823f37e1..61bb4f1fc557bd820ed5fb4151bf3a0167479783 100644 GIT binary patch delta 23 fcmX>ren)(Q3J-I!lV=DA2gmJ){tq@PuHgm%VL1qS delta 10 Rcmca3epY;f%0}ar+yEI!1S9|e diff --git a/src/lay/lay/doc/images/drc_extent_refs20.png b/src/lay/lay/doc/images/drc_extent_refs20.png index 1edb560b0fccd6d13352b5f71e31e2d178c64139..29c074218cacf2bae227ec02bb84490d2729951d 100644 GIT binary patch delta 23 fcmX>lc~f$N3J-I!lV=DA2gmJ){tq@PuH*p#VFUlc~f$N3J-I!lV=DA2gmJ){tq@PuH*p#VFU_$fX?Wux&`ZU7g-1StRj diff --git a/src/lay/lay/doc/images/drc_extent_refs23.png b/src/lay/lay/doc/images/drc_extent_refs23.png index 5497e24f097aaf1778d34b874bd3d40a59cdc08d..d1e2dc0e6c92231f35e2294421c3f936c95796f5 100644 GIT binary patch delta 23 fcmaDQ{!@H{3J-I!lV=DA2gmJ){tq@Pp5z7qW^V}4 delta 10 Rcmew<{z`m;%0}a3+yEPl1bYAg diff --git a/src/lay/lay/doc/images/drc_extent_refs24.png b/src/lay/lay/doc/images/drc_extent_refs24.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extent_refs25.png b/src/lay/lay/doc/images/drc_extent_refs25.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extent_refs26.png b/src/lay/lay/doc/images/drc_extent_refs26.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extent_refs27.png b/src/lay/lay/doc/images/drc_extent_refs27.png index 5497e24f097aaf1778d34b874bd3d40a59cdc08d..d1e2dc0e6c92231f35e2294421c3f936c95796f5 100644 GIT binary patch delta 23 fcmaDQ{!@H{3J-I!lV=DA2gmJ){tq@Pp5z7qW^V}4 delta 10 Rcmew<{z`m;%0}a3+yEPl1bYAg diff --git a/src/lay/lay/doc/images/drc_extent_refs30.png b/src/lay/lay/doc/images/drc_extent_refs30.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extent_refs31.png b/src/lay/lay/doc/images/drc_extent_refs31.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extents1.png b/src/lay/lay/doc/images/drc_extents1.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_extents2.png b/src/lay/lay/doc/images/drc_extents2.png index 1835670787fe2bc52c65d7247a230bcb33b114e0..203a4aa22783dcbac01308fb0e3a31fce8fba305 100644 GIT binary patch delta 23 fcmew<-XSqTg@?J=$uoq5gX8u?{|6ftZ*T(uUbP5* delta 10 RcmeB>_$fX?Wux&`ZU7g-1StRj diff --git a/src/lay/lay/doc/images/drc_holes.png b/src/lay/lay/doc/images/drc_holes.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_hulls.png b/src/lay/lay/doc/images/drc_hulls.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_in.png b/src/lay/lay/doc/images/drc_in.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_inside.png b/src/lay/lay/doc/images/drc_inside.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_inside_part.png b/src/lay/lay/doc/images/drc_inside_part.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_interacting.png b/src/lay/lay/doc/images/drc_interacting.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_join1.png b/src/lay/lay/doc/images/drc_join1.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_join2.png b/src/lay/lay/doc/images/drc_join2.png index e7916b61b39e85d9e2f750f26c138cbb48acbd3a..48f1b93fd559ece1d50660a5aab3c317b43e940d 100644 GIT binary patch delta 23 fcmaDM{#|^63J-I!lV=DA2gmJ){tq@P9_I!CW)ld? delta 10 Rcmew^{z80$%0}ZO+yEP71a$xa diff --git a/src/lay/lay/doc/images/drc_merged1.png b/src/lay/lay/doc/images/drc_merged1.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_merged2.png b/src/lay/lay/doc/images/drc_merged2.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_merged3.png b/src/lay/lay/doc/images/drc_merged3.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_merged4.png b/src/lay/lay/doc/images/drc_merged4.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_middle1.png b/src/lay/lay/doc/images/drc_middle1.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_moved1.png b/src/lay/lay/doc/images/drc_moved1.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_not1.png b/src/lay/lay/doc/images/drc_not1.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_not2.png b/src/lay/lay/doc/images/drc_not2.png index 1795d38c366af3acbf502c3fdd4ab01d96a3c842..57f0e81fdf8fa0cc09473c855a31b5cf046cf9b1 100644 GIT binary patch delta 23 fcmcaD{z`m;3J-I!lV=DA2gmJ){tq@P?&1ajW4#EY delta 10 RcmaDQep`Hk%0}bu+yEM21Wo_| diff --git a/src/lay/lay/doc/images/drc_not3.png b/src/lay/lay/doc/images/drc_not3.png index 1795d38c366af3acbf502c3fdd4ab01d96a3c842..57f0e81fdf8fa0cc09473c855a31b5cf046cf9b1 100644 GIT binary patch delta 23 fcmcaD{z`m;3J-I!lV=DA2gmJ){tq@P?&1ajW4#EY delta 10 RcmaDQep`Hk%0}bu+yEM21Wo_| diff --git a/src/lay/lay/doc/images/drc_not_in.png b/src/lay/lay/doc/images/drc_not_in.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_not_inside.png b/src/lay/lay/doc/images/drc_not_inside.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_not_interacting.png b/src/lay/lay/doc/images/drc_not_interacting.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_not_outside.png b/src/lay/lay/doc/images/drc_not_outside.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_not_overlapping.png b/src/lay/lay/doc/images/drc_not_overlapping.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_or1.png b/src/lay/lay/doc/images/drc_or1.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_or2.png b/src/lay/lay/doc/images/drc_or2.png index e7916b61b39e85d9e2f750f26c138cbb48acbd3a..48f1b93fd559ece1d50660a5aab3c317b43e940d 100644 GIT binary patch delta 23 fcmaDM{#|^63J-I!lV=DA2gmJ){tq@P9_I!CW)ld? delta 10 Rcmew^{z80$%0}ZO+yEP71a$xa diff --git a/src/lay/lay/doc/images/drc_outside.png b/src/lay/lay/doc/images/drc_outside.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_outside_part.png b/src/lay/lay/doc/images/drc_outside_part.png index 1795d38c366af3acbf502c3fdd4ab01d96a3c842..57f0e81fdf8fa0cc09473c855a31b5cf046cf9b1 100644 GIT binary patch delta 23 fcmcaD{z`m;3J-I!lV=DA2gmJ){tq@P?&1ajW4#EY delta 10 RcmaDQep`Hk%0}bu+yEM21Wo_| diff --git a/src/lay/lay/doc/images/drc_overlap1.png b/src/lay/lay/doc/images/drc_overlap1.png index 2b5c608f32506ba5e52ebbd914df671340bdd412..db427d86f41efd20f57a6ad3bd949e116133b169 100644 GIT binary patch delta 23 fcmbOvvQT7#3J-I!lV=DA2gmJ){tq@Pe&hrIS04yK delta 10 RcmZ1|GD&2D%0}aNoB$Pi1M2_) diff --git a/src/lay/lay/doc/images/drc_overlap2.png b/src/lay/lay/doc/images/drc_overlap2.png index 2b5c608f32506ba5e52ebbd914df671340bdd412..db427d86f41efd20f57a6ad3bd949e116133b169 100644 GIT binary patch delta 23 fcmbOvvQT7#3J-I!lV=DA2gmJ){tq@Pe&hrIS04yK delta 10 RcmZ1|GD&2D%0}aNoB$Pi1M2_) diff --git a/src/lay/lay/doc/images/drc_overlapping.png b/src/lay/lay/doc/images/drc_overlapping.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_raw1.png b/src/lay/lay/doc/images/drc_raw1.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_raw2.png b/src/lay/lay/doc/images/drc_raw2.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_raw3.png b/src/lay/lay/doc/images/drc_raw3.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_rotated1.png b/src/lay/lay/doc/images/drc_rotated1.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_rounded_corners.png b/src/lay/lay/doc/images/drc_rounded_corners.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_scaled1.png b/src/lay/lay/doc/images/drc_scaled1.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_separation1.png b/src/lay/lay/doc/images/drc_separation1.png index 2b5c608f32506ba5e52ebbd914df671340bdd412..db427d86f41efd20f57a6ad3bd949e116133b169 100644 GIT binary patch delta 23 fcmbOvvQT7#3J-I!lV=DA2gmJ){tq@Pe&hrIS04yK delta 10 RcmZ1|GD&2D%0}aNoB$Pi1M2_) diff --git a/src/lay/lay/doc/images/drc_sized1.png b/src/lay/lay/doc/images/drc_sized1.png index 396ef0f0b0c449cb49fd4344b64e0396cde32131..261fe8010e564a46f764e8f23d4f730e7b45926e 100644 GIT binary patch delta 23 fcmZ24xleL}3J-I!lV=DA2gmJ){tq@P_VEA!Tq+18 delta 10 Rcmdldxn6RD%0}aE9sm~+1J3{e diff --git a/src/lay/lay/doc/images/drc_sized2.png b/src/lay/lay/doc/images/drc_sized2.png index 459dc44ae6863f317bef577e96713198c5530732..936c7167e33a1cf4ebe516c28e84bc0259533bdc 100644 GIT binary patch delta 23 fcmbO(u}oru3J-I!lV=DA2gmJ){tq@Pe&YrJSg#0K delta 10 RcmZ1`Ftu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_with_angle2.png b/src/lay/lay/doc/images/drc_with_angle2.png index e7916b61b39e85d9e2f750f26c138cbb48acbd3a..48f1b93fd559ece1d50660a5aab3c317b43e940d 100644 GIT binary patch delta 23 fcmaDM{#|^63J-I!lV=DA2gmJ){tq@P9_I!CW)ld? delta 10 Rcmew^{z80$%0}ZO+yEP71a$xa diff --git a/src/lay/lay/doc/images/drc_with_angle3.png b/src/lay/lay/doc/images/drc_with_angle3.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_with_angle4.png b/src/lay/lay/doc/images/drc_with_angle4.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_xor1.png b/src/lay/lay/doc/images/drc_xor1.png index 1190654292634dfe34e96a4040c4bf5659e2c014..f3ba86b26506efd4bc34680df1c49f41d6621e6c 100644 GIT binary patch delta 23 fcmdlZaav-63J-I!lV=DA2gmJ){tq@PO7j2!T~-Il delta 10 RcmX>tu}5Np%0^=e9sm~w19SiY diff --git a/src/lay/lay/doc/images/drc_xor2.png b/src/lay/lay/doc/images/drc_xor2.png index e7916b61b39e85d9e2f750f26c138cbb48acbd3a..48f1b93fd559ece1d50660a5aab3c317b43e940d 100644 GIT binary patch delta 23 fcmaDM{#|^63J-I!lV=DA2gmJ){tq@P9_I!CW)ld? delta 10 Rcmew^{z80$%0}ZO+yEP71a$xa From 745696507ffb1f8942d37b8c0c5689d27fcf3660 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 9 Mar 2019 09:59:23 +0100 Subject: [PATCH 328/335] Two bug fixes in DRC related to flatten: - Merge semantics wasn't transferred to flat region - Merge semantics wasn't set at all in deep region --- src/db/db/dbRegion.cc | 3 +- src/db/db/gsiDeclDbRegion.cc | 2 +- src/drc/drc/built-in-macros/drc.lym | 2 +- testdata/drc/drcSimpleTests_4.drc | 47 ++++++++++++++++++++++------ testdata/drc/drcSimpleTests_au4.oas | Bin 1369 -> 1458 bytes 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index ae5ca987e..d362a7af9 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -310,9 +310,8 @@ Region::flat_region () if (! region) { region = new FlatRegion (); if (mp_delegate) { + region->RegionDelegate::operator= (*mp_delegate); // copy basic flags region->insert_seq (begin ()); - } - if (mp_delegate) { region->set_is_merged (mp_delegate->is_merged ()); } set_delegate (region); diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 5b37d1790..965fc3ce1 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -135,7 +135,7 @@ static db::Region *new_si2 (const db::RecursiveShapeIterator &si, const db::ICpl static db::Region *new_sid2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans, double area_ratio, size_t max_vertex_count) { - return new db::Region (si, dss, trans, area_ratio, max_vertex_count); + return new db::Region (si, dss, trans, true, area_ratio, max_vertex_count); } static std::string to_string0 (const db::Region *r) diff --git a/src/drc/drc/built-in-macros/drc.lym b/src/drc/drc/built-in-macros/drc.lym index ea52c850f..9de7b23dd 100644 --- a/src/drc/drc/built-in-macros/drc.lym +++ b/src/drc/drc/built-in-macros/drc.lym @@ -1360,7 +1360,7 @@ CODE # It is an alias for the "&" operator. # # This method is available for polygon and edge layers. - # If the first operand is an edge layer and the second is an edge layer, the + # If the first operand is an edge layer and the second is a polygon layer, the # result will be the edges of the first operand which are inside or on the # borders of the polygons of the second operand. # diff --git a/testdata/drc/drcSimpleTests_4.drc b/testdata/drc/drcSimpleTests_4.drc index 280fafc98..b729ee968 100644 --- a/testdata/drc/drcSimpleTests_4.drc +++ b/testdata/drc/drcSimpleTests_4.drc @@ -33,18 +33,45 @@ l2.output(2, 0) l1_flat.output(11, 0) l2_flat.output(12, 0) -l1.and(l2).output(1000, 0) -l1_flat.and(l2).output(1001, 0) -l1.and(l2_flat).output(1002, 0) -l1_flat.and(l2_flat).output(1003, 0) +r = l1.and(l2) +r.output(1000, 0) +r.extents.output(1100, 0) -l1.separation(l2, 0.3).output(1020, 0) -l1_flat.separation(l2, 0.3).output(1021, 0) -l1.separation(l2_flat, 0.3).output(1022, 0) -l1_flat.separation(l2_flat, 0.3).output(1023, 0) +r = l1_flat.and(l2) +r.output(1001, 0) +r.extents.output(1101, 0) + +r = l1.and(l2_flat) +r.output(1002, 0) +r.extents.output(1102, 0) + +r = l1_flat.and(l2_flat) +r.output(1003, 0) +r.extents.output(1103, 0) + +r = l1.separation(l2, 0.3) +r.output(1020, 0) +r.extents.output(1120, 0) + +r = l1_flat.separation(l2, 0.3) +r.output(1021, 0) +r.extents.output(1121, 0) + +r = l1.separation(l2_flat, 0.3) +r.output(1022, 0) +r.extents.output(1122, 0) + +r = l1_flat.separation(l2_flat, 0.3) +r.output(1023, 0) +r.extents.output(1123, 0) + +r = l1.space(0.5) +r.output(1010, 0) +r.extents.output(1110, 0) -l1.space(0.2).output(1010, 0) l1.flatten -l1.space(0.2).output(1011, 0) +r = l1.space(0.5) +r.output(1011, 0) +r.extents.output(1111, 0) diff --git a/testdata/drc/drcSimpleTests_au4.oas b/testdata/drc/drcSimpleTests_au4.oas index 7a7a8d6b0d11f0dc30601322f9f3c14735a07442..3a6f5f206a560e7042bc91cf01caaa88e640e779 100644 GIT binary patch delta 280 zcmcb~wTXK}5F?8S;~9?0L5wP*qL!c7S(tb8++cas$+(l}Cd*{z$)=mj7#B0LoaF$j zVPn=}I>#|tnpw+Pr1CsaWDV0sr5|}BHEWp8aLkCAAj2S1`GDgM>j}mV&li>=F$b6@ z*lkc^5D97Ec;Un#QgMRu0P{(y4*?SQnhDH5xE2JyP+$}n)bIxUZtlg9%YJ=jLec{g(fDXKlte+ER0Mq0|mT!{_SlF2u7y!LmV^078 delta 64 zcmV-G0KfmT3)u>=QUQ}z0VK1J0iywvNdp&?cLNm!6*>I}llTKO0u?-y(*q;|J^qu@ W16h;L16% Date: Sat, 9 Mar 2019 19:40:38 +0100 Subject: [PATCH 329/335] A few bug fixes and test updates - edge pairs are normalized before turning them into polygons. This makes flat and deep implementation more consistent. - deep region and flat regions were not cooperating in geo checks - unnamed layers are not registered in make_layer - this does not make sense and will just hold a fake ref - tests now use GDS to represent texts after transformation (with orientation, OASIS can't do this) - texts are more consistently handled in the tests - test debug output is not written in the same format than golden data unless special normalization is requested. - a non-orientable polygon was converted to orientable in a text because this can be represented in GDS consistently - DRC testsuite uses "polygons" instead of "input" to achieve identical behavior for deep and flat mode with respect to texts - dbRegionTests are updated because texts are not allowed for non-original layers too --- src/db/db/dbAsIfFlatEdgePairs.cc | 2 +- src/db/db/dbDeepRegion.cc | 2 +- src/db/db/dbDeepShapeStore.cc | 2 +- src/db/db/dbFlatEdgePairs.cc | 2 +- src/db/db/dbLayoutToNetlist.cc | 8 +++-- src/db/db/dbLayoutToNetlist.h | 36 +++++++++++++------- src/db/db/dbTestSupport.cc | 6 +++- src/db/unit_tests/dbLayoutToNetlistTests.cc | 6 +++- src/drc/unit_tests/drcBasicTests.cc | 2 +- src/drc/unit_tests/drcSimpleTests.cc | 16 ++++----- testdata/drc/drcBasicTests_au.gds | Bin 0 -> 2138 bytes testdata/drc/drcBasicTests_au.oas | Bin 669 -> 0 bytes testdata/drc/drcSimpleTests_1.drc | 2 +- testdata/drc/drcSimpleTests_au1.gds | Bin 0 -> 426 bytes testdata/drc/drcSimpleTests_au1.oas | Bin 431 -> 0 bytes testdata/drc/drcSimpleTests_au2.gds | Bin 0 -> 12460 bytes testdata/drc/drcSimpleTests_au2.oas | Bin 1869 -> 0 bytes testdata/drc/drcSimpleTests_au3.gds | Bin 0 -> 316 bytes testdata/drc/drcSimpleTests_au3.oas | Bin 437 -> 0 bytes testdata/drc/drcSimpleTests_au4.gds | Bin 0 -> 10580 bytes testdata/drc/drcSimpleTests_au4.oas | Bin 1458 -> 0 bytes testdata/drc/drcSimpleTests_au5.gds | Bin 0 -> 1708 bytes testdata/drc/drcSimpleTests_au5.oas | Bin 464 -> 0 bytes testdata/drc/drcSimpleTests_au6.gds | Bin 0 -> 1626 bytes testdata/drc/drcSimpleTests_au6.oas | Bin 514 -> 0 bytes testdata/drc/drcSimpleTests_au7.gds | Bin 0 -> 3290 bytes testdata/drc/drcSimpleTests_au7.oas | Bin 611 -> 0 bytes testdata/drc/drcSimpleTests_au8.gds | Bin 0 -> 2792 bytes testdata/drc/drcSimpleTests_au8.oas | Bin 656 -> 0 bytes testdata/drc/drcSuiteTests.drc | 12 +++---- testdata/ruby/dbRegionTest.rb | 16 +++------ 31 files changed, 64 insertions(+), 48 deletions(-) create mode 100644 testdata/drc/drcBasicTests_au.gds delete mode 100644 testdata/drc/drcBasicTests_au.oas create mode 100644 testdata/drc/drcSimpleTests_au1.gds delete mode 100644 testdata/drc/drcSimpleTests_au1.oas create mode 100644 testdata/drc/drcSimpleTests_au2.gds delete mode 100644 testdata/drc/drcSimpleTests_au2.oas create mode 100644 testdata/drc/drcSimpleTests_au3.gds delete mode 100644 testdata/drc/drcSimpleTests_au3.oas create mode 100644 testdata/drc/drcSimpleTests_au4.gds delete mode 100644 testdata/drc/drcSimpleTests_au4.oas create mode 100644 testdata/drc/drcSimpleTests_au5.gds delete mode 100644 testdata/drc/drcSimpleTests_au5.oas create mode 100644 testdata/drc/drcSimpleTests_au6.gds delete mode 100644 testdata/drc/drcSimpleTests_au6.oas create mode 100644 testdata/drc/drcSimpleTests_au7.gds delete mode 100644 testdata/drc/drcSimpleTests_au7.oas create mode 100644 testdata/drc/drcSimpleTests_au8.gds delete mode 100644 testdata/drc/drcSimpleTests_au8.oas diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc index 7803bbb23..0876cdc0d 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.cc +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -292,7 +292,7 @@ AsIfFlatEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); for (EdgePairsIterator e (begin ()); ! e.at_end (); ++e) { - shapes.insert (e->to_simple_polygon (enl)); + shapes.insert (e->normalized ().to_simple_polygon (enl)); } } diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 5fa74efe3..0ee8b7195 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -247,7 +247,7 @@ DeepRegion::has_valid_polygons () const bool DeepRegion::has_valid_merged_polygons () const { - return merged_semantics (); + return false; } const db::RecursiveShapeIterator * diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 6ee3172bf..b2788f090 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -873,7 +873,7 @@ DeepShapeStore::insert_as_polygons (const DeepLayer &deep_layer, db::Layout *int if (s->is_edge_pair ()) { - out.insert (s->edge_pair ().to_simple_polygon (enl)); + out.insert (s->edge_pair ().normalized ().to_simple_polygon (enl)); } else if (s->is_path () || s->is_polygon () || s->is_box ()) { diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc index e4a8d50f8..87d8eb689 100644 --- a/src/db/db/dbFlatEdgePairs.cc +++ b/src/db/db/dbFlatEdgePairs.cc @@ -185,7 +185,7 @@ FlatEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type into { db::Shapes &out = layout->cell (into_cell).shapes (into_layer); for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { - out.insert (p->to_simple_polygon (enl)); + out.insert (p->normalized ().to_simple_polygon (enl)); } } diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 9028e5abe..97cce0100 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -129,7 +129,9 @@ db::Region *LayoutToNetlist::make_layer (const std::string &n) si.shape_flags (db::ShapeIterator::Nothing); std::auto_ptr region (new db::Region (si, dss ())); - register_layer (*region, n); + if (! n.empty ()) { + register_layer (*region, n); + } return region.release (); } @@ -140,7 +142,9 @@ db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::st si.shape_flags (db::ShapeIterator::All); std::auto_ptr region (new db::Region (si, dss ())); - register_layer (*region, n); + if (! n.empty ()) { + register_layer (*region, n); + } return region.release (); } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 95649d014..9bca6b617 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -305,6 +305,30 @@ public: */ void set_netlist_extracted (); + /** + * @brief Gets the internal DeepShapeStore object + * + * This method is intended for special cases, i.e. for the master + * LayoutToNetlist object in the DRC environment. The DSS provided + * for DRC needs to be initialized properly for text representation. + */ + db::DeepShapeStore &dss () + { + tl_assert (mp_dss.get () != 0); + return *mp_dss; + } + + /** + * @brief Gets the internal DeepShapeStore object (const version) + * + * See the non-const version for details. + */ + const db::DeepShapeStore &dss () const + { + tl_assert (mp_dss.get () != 0); + return *mp_dss; + } + /** * @brief Gets the internal layout */ @@ -526,18 +550,6 @@ private: bool m_is_flat; db::DeepLayer m_dummy_layer; - db::DeepShapeStore &dss () - { - tl_assert (mp_dss.get () != 0); - return *mp_dss; - } - - const db::DeepShapeStore &dss () const - { - tl_assert (mp_dss.get () != 0); - return *mp_dss; - } - void init (); size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path); void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index 3f7798bef..b8b1af7d5 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -59,9 +59,13 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: if (norm == WriteGDS2) { tmp_file = _this->tmp_file (tl::sprintf ("tmp_%x.gds", hash)); options.set_format ("GDS2"); - } else { + } else if (norm == WriteOAS) { tmp_file = _this->tmp_file (tl::sprintf ("tmp_%x.oas", hash)); options.set_format ("OASIS"); + } else { + // write the temp file in the same format than the au file + tmp_file = _this->tmp_file (tl::sprintf ("tmp_%x." + tl::extension (au_file), hash)); + options.set_format_from_filename (tmp_file); } { diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 681dff981..730d31ae0 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -2111,7 +2111,11 @@ TEST(9_FlatExtractionWithExternalDSS) db::Cell &tc = ly.cell (*ly.begin_top_down ()); - db::DeepShapeStore dss; + // NOTE: we use a DSS from a LayoutToNetlist object - this one is initialized properly + // with the text representation settings. + db::LayoutToNetlist l2n_master; + db::DeepShapeStore &dss = l2n_master.dss (); + db::LayoutToNetlist l2n (&dss); std::auto_ptr rbulk (new db::Region ()); diff --git a/src/drc/unit_tests/drcBasicTests.cc b/src/drc/unit_tests/drcBasicTests.cc index a5978b6d6..98401cb0c 100644 --- a/src/drc/unit_tests/drcBasicTests.cc +++ b/src/drc/unit_tests/drcBasicTests.cc @@ -30,7 +30,7 @@ TEST(1) std::string input = tl::testsrc (); input += "/testdata/drc/drctest.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcBasicTests_au.oas"; + au += "/testdata/drc/drcBasicTests_au.gds"; std::string output = this->tmp_file ("tmp.gds"); diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 4d8c6b11d..a8f69fb4f 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -31,7 +31,7 @@ TEST(1) rs += "/testdata/drc/drcSimpleTests_1.drc"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au1.oas"; + au += "/testdata/drc/drcSimpleTests_au1.gds"; std::string output = this->tmp_file ("tmp.gds"); @@ -71,7 +71,7 @@ TEST(2) input += "/testdata/drc/drctest.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au2.oas"; + au += "/testdata/drc/drcSimpleTests_au2.gds"; std::string output = this->tmp_file ("tmp.gds"); @@ -111,7 +111,7 @@ TEST(3_Flat) input += "/testdata/drc/drctest.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au3.oas"; + au += "/testdata/drc/drcSimpleTests_au3.gds"; std::string output = this->tmp_file ("tmp.gds"); @@ -151,7 +151,7 @@ TEST(4_Hierarchical) input += "/testdata/drc/drctest.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au4.oas"; + au += "/testdata/drc/drcSimpleTests_au4.gds"; std::string output = this->tmp_file ("tmp.gds"); @@ -191,7 +191,7 @@ TEST(5_FlatAntenna) input += "/testdata/drc/antenna_l1.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au5.oas"; + au += "/testdata/drc/drcSimpleTests_au5.gds"; std::string output = this->tmp_file ("tmp.gds"); @@ -231,7 +231,7 @@ TEST(6_HierarchicalAntenna) input += "/testdata/drc/antenna_l1.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au6.oas"; + au += "/testdata/drc/drcSimpleTests_au6.gds"; std::string output = this->tmp_file ("tmp.gds"); @@ -271,7 +271,7 @@ TEST(7_AntennaWithDiodes) input += "/testdata/drc/antenna_l1.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au7.oas"; + au += "/testdata/drc/drcSimpleTests_au7.gds"; std::string output = this->tmp_file ("tmp.gds"); @@ -311,7 +311,7 @@ TEST(8_TextsAndPolygons) input += "/testdata/drc/texts.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au8.oas"; + au += "/testdata/drc/drcSimpleTests_au8.gds"; std::string output = this->tmp_file ("tmp.gds"); diff --git a/testdata/drc/drcBasicTests_au.gds b/testdata/drc/drcBasicTests_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..594ba23d0655028da94e71289f1c31982e1983c8 GIT binary patch literal 2138 zcmcJQv2GJV5QfKR`*MImL0CW}KLA2Vq=*EOA`~NvAS{t0MT96)P*CsyDJfEbL_tqO zfs{M~1qDTvIt2vy?9TqPGrO}CQV=Xttq@E! zsYqw&5>3&y(fBC24OUoV_jI2G3GC(S*$I-Gm`G;;^jBO0!zrCL7gV0BfjnLQee0_{V2ZS#CjbzUrp&3{rz_NLyhgjT^Rk${@3|S`MWbFI-s00 z`6WbLI-b%$JF0)UE;(+ACdZ? zhoep);#2?p_5VZvu=rj6U4M``tbZVX=pP{B)A7vsj@8fZtud#5DgA%wKPi7`pvGWU l|Iu-c-@kEH3QzsUWz0pz+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4AcB?2(GiHXSQ(vo8APoa8CX~j z6?7OicpqED*eJQvK&0XZ%M#v+%m?j6EtwcuW(aQJe969n^918dOHm6(jsw(Zgo&{_hl@8j86rADM!`kT}QZa=qrQ%)rSY%pk|0hs^+>@@d2w)}&o%MSeo zv!g;7WLWX&V`B^P4`5(m;b353<7HytVqoKAVqjoU5nu+AP{90xfdPcsFTg~=G>ae( zeatWZ|9>$7q~`zsHx(d#5E?3uO`i_PKK=z@bHOyz9tH-X7zPFy4U&P%+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4AcB`c)S8Qdg{iBy;TPit{)e%m y7A#C1wSBb=3?dm9_)jptR1mqbhxLFRgQy`l1Iq>e1@ag8FY+@mHH>7yzyJV#FRG^i diff --git a/testdata/drc/drcSimpleTests_au2.gds b/testdata/drc/drcSimpleTests_au2.gds new file mode 100644 index 0000000000000000000000000000000000000000..44b1433abf5ad1c9da5b54d3cc47d26a6acf10ae GIT binary patch literal 12460 zcmb_iU94PX6+L%m?({aPTCkQ@%R>{z1Zax}D1^pP;{$577eOdcpehgbC-|h{fuvPQ zltc+iqlr9NjER`|0-9iq38Zs&G>+I#QkUT1%6&bQB;b7tln zn@ra2K4+b^*WUYl=VvBqCbin3WXDWx!Y)r09b|%}xzmvII^S~!Ro+Ml9vme|4 zNs`>Sx$a#573bP# z6}v~ByFBk)ujky_^BHn``)tyfMmYUQ&lNm!e}``rhDJ`%T6*@B9E1;TD+z+sEyvvrpXL;2g2N z!&k~9d359}%i|x$@h>enckT(rg)Qokc>Z>c6Bp<7_yc@HJFw`H$2s>zp`U%5>r%%@ zoLm0B;>EuuN$n5L5v6mZ|Fo28DH2^&PV-FXCUgoz9~b-OX{EIk6c&o(45m=pYlByWPIh0a{Z-M)dxZ! z93u2}dpRF<0u7`4bt>@$3HlPV1NUSmqQceU^V2 zVy<<&$=~qNV}7s}gdfK3{M9e0{>&zv{}=x*^%k9rI|WaP-U;z@r|K?l^!U2?lKEQt zuHe^2Px^G>CDmPC_IRt{ul2Kxza=kj>-Feg4C>wbrv`)_+)^L*TeqS4S)42<)APU}X2IWQ$av?i zu>O{R`QOS1g1`Ey48hm!hR-`E)t`9&8ipU?znS@$^F(H@eaz$Af4 zqd&R-fG>KJ``NCO?{4uA{E_(oQ-F-`HvB8|_3yo^_3vM&^(Qjkd$pVo|D89r{@uM= zef@OXz>?8+!f$p^Nj62z}jd`WNt}-w(>X?NVgj ziHsNO`~0KzQ+|~G`$GR=|60#z{{~pRei(j!{lNTS{Q{QsPx%G?8|X5RDt*t7(BGQ* z*Xs}ZgzFI2PuzbDka0Ud0bkdD_{4P>zH$FSWIQ*&%J=QI-+cuxPS z(0@3;!`F|&djshW-1=$w zm_M%5T(^jf=hkoK`~2*r|`ko)5zdiG>b1(kV`Nz72`6n`-^B?ea?%@-22;Z20 zBI7y#%J==p{8NXzM8*sCy?^LZhq^?@bNcTO{bT;11B5Q_pNNd-^aH-mJ#;XK&<(F2 zj2G&A|Izy1KXj=xrv3*q|2qHeV;Gs2xBdgZ_8tDw2lz(+2gtbful%6@QTjpuC)B?> z^RMq4I*T)ZG6VzWa;ROYYx+7ax%IGpDd#QOE2Dd#8P59UMf9r+5L-#r#TzXyED{~_6bx5uI9|H1a~{15oj zzo&Hl;`@jG1z88=Jp{1i{e$7JeN*=X7QMgF?PmTBAMY*jo)(DrxA0G7Joo-L;Hy8` zp4Opzv!J~HATnOgm;OoKz0->QYX#-~3z6~OX_Nl})4yA!{_zdPhd=zo4n{ykIj?~UdAsXOHP1?PpG z9&=udV6g|BwNY9_6C0ivrCQ3Omh?Mh-52GnlvSBwDND~lW|*r+DJ#yGuxIe02BUmMsFJ)DBc?64ExjB5G+?V~GnW^CjR{k36Ygoz} zx>AQ-9>JoPT;D_1;V54tSk&UZgZEX+s`NF2MXj8#2cvwAU{NdQtCUsgYXpm0IbRP& z`5M8ZR?b%`tJ2p97PaWhU;mGIU+M1}`Y+3U`5AeiaZv7W`*L6Vs|*p>?K;O*eC#*) zMX5i|IDF`FzdWD$w9kyG|K;lZ{kcK^L)UL{i@U(1QjK}3LeHGNr7XD{emc9i&-JyG zRoT}HmS!eCpU*NZ>WkYeIXx^Yg56ok@rM~Q-g?;7^$SzZMInEJxiI{QxiEarb*ew! zT#!H6T$HjZ=c1HVITsbIin$;wJ>Mhd;)`Z34$B-hA8*p0QUCw| literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au2.oas b/testdata/drc/drcSimpleTests_au2.oas deleted file mode 100644 index 787eddb7abd4fcfd974d3c28eb8de982f48b8902..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1869 zcmd^ScqhMC|Rz=M;?o~go;d9 zV$yD~7Ioon8B^knG<$IynpLBLq;q9++TzN9xs4^7h-k3*_ncc0#6KbN(VLu;b3Whi z@6SCww-5Vj_SZhvSnaFzS&YD!m$}fqjMhKJN|DdMzq-ZmtKHjBv&UcC&{VUx<{AI9 zwR@WOVWHSkoRgay{?1UW7=8Yx#;5&H)zsIUdAj1+By!c&bUZ4;lNO{@0uqD|+k4$z zb)TGupyPsz1X98oOPG&)^E(01q%B*L|2%ifnIAw4O14BP=Rmq_p0xdFoXLxuH7T}T zsQ|1=DIq1Kg%Hn1(l$b}&6EpK`>5f*JZcz+`yLYLH>#5d!VM%5;rK^=urIA~=v zNsWnVA>Bw?G--syy(BO%YtlshLKz7p@R-`-UCV7LK)4FO4{PyxL5IfCkxKo zViczA=ecjWxk5Hi*%|q&a2+3j7!^|XSt`s7z^ph3)Aks*z}+om^E4F(HwXi8o+>wK zu0goEBix{rFUkVneK=`*X&HitCvAviewMjK<);!8s8w##^NVakA$|)9|0-X%k%8aj zMTL60uzI5)Q%YAsth?#z2yQ#n?KUBGmz88*!KxZ`Q`)OjM0!FPkt09{hseP&MxWw) zj-hhW@`1R-zqy+nfDPaUE`ilMj+4|fCjP(=?{jVY*gDX89HX{n zqn^9ss8!GXIBWow{TMA9BSu=EdM=8zKJ)wuLP$i~DK6nSGcsa3aho}`V! z$K0#R@FOH}0!H}f=z(O;;5PPRCRuIu(wUVA;Y!d%KJB=iB7OU*&5a~$Z z)lYzQT%lL{YinBQt0MH&3$j-+5&_a%=jGV;f*lCr=Dw4NaAs+ z-xd^OG;R`;z{_&d_P+QHRj#eeetpf=@!1-O8<@c7bbNseJShw?vMq%B^m<`SOs^NR z1Vu&w-r7cqC%xu!Az&)&Dr^a1s=Anq5dh7}x?r96TCY6BlvB~63@*|xiXt_O>fFIg9Yey2Oad6p#8)Qnne3} zVzo09V)sd@5cs(H9MS6To`!D6C71G`z+Y>Ry6@z@Lll?5zwJC~jk-g1?;6-{u6@L* zNxGE;ri8PWA`7%N*nAO-;?>Sd(pa1{> diff --git a/testdata/drc/drcSimpleTests_au3.gds b/testdata/drc/drcSimpleTests_au3.gds new file mode 100644 index 0000000000000000000000000000000000000000..5f919587f5900e8c583bb68aa4779e13c5bdaf55 GIT binary patch literal 316 zcmZQzV_;&6V31*CVt>rQ%)rSY#GuF^iOgo;U}E#}bYfr-VP>^+>@@d2w)~By%MSeo zv!enSWLRhA@ecqZ1{Mwm1~y(M=9dg?d`t`s3_1eLKr-e3|BpQo`s)n_2F3~h p|HEjQIFM!$WME)nXTWO{2OFE8YdBCH)Sef#vj^lIuzOfo7yz_yLhAqk literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au3.oas b/testdata/drc/drcSimpleTests_au3.oas deleted file mode 100644 index 7f496f52f3da5fd028c71bb6cb4ee75ddd3ce88b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 437 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>FaudKf;`NAuHit64afr`W*7|;W(FzY iW#VNNsRp`Z4bvHp6O1n{c^O2iUn00n4I>#aFaQ8BM5);T diff --git a/testdata/drc/drcSimpleTests_au4.gds b/testdata/drc/drcSimpleTests_au4.gds new file mode 100644 index 0000000000000000000000000000000000000000..1f3124594f337cea35454c64a5235480b0df1445 GIT binary patch literal 10580 zcmbuFUuay%6~>Qtuhwg8Wvyhb{#hx3rcjC?3N=KgZn0w*9IDhQ6{$&G;?xRFA<&fe zArF0U!B7YC;L<`1X~Bg+QfR@@l=vZ~E`*YYJ_MSF1OjPD03ON{rmZ)y@$@K-Xw29C=VAX^J!kYtko;oi!2J)2^rK%7rvteouMh z@ihJH!8Gl5)AaKfa$5WHdy))qe$EV^lg_2-(sAXDM5OObni|c%bX@hV^*F{DM~nKK zr(OTXWaGerc(2uo^PMmmHts? zMrr-PUe7z`MsPVzFP*|jz{}7wR^yfZYByeOYR1?3nr8fJMtjbsQ$797(;sJxnI)G` zhJT;#!|ctbsd0Hao9T?Evl+=&hULf2=-TTVKY8^volzslrR_cAb9&~1en|RXYvnY1 zk^jqYrRnv|h{$f4n zk$UEfdZXDR^Xu~W@sj!{>7R3Kqi6cASeB6d)z4&6W>WyZzg|g>znLd&&mAO^!3$Tk9Gec za=sfJ0N+P_|5>>5PzWWzODUM{;9f~e^RdeOYPbo=&$+y)4geW?Irkq9{t}{ zcJILs4f~CL<+YbozfB5HkhVKPaELte-3JcL&r7mHq&cFeN{hK3dqJf03w_qcwuw2B zyUuTFYfSZV@1O3!?_rQ1Jc%n#WcSfvR9;p@Mh=SX{F%tEFNzF(9bWHIj($h&S-ZbW z4`f-vc_-3-Tx9!kWedCea2L9t<50x`X|#4Q@*G`BoeAzL9KCW9p|9=UruWWd&T2cJNx&)rMlKh5oX0^h?D%rdf%CAVl8w#Zt3RXZ zHdL662l?UMg*i}u0p}ri3gewP+dg|y#AyAHvtIGK`{vq{tT^1Oz)6gcxiF3$Lat`8 zGi79T@?#u3g#CYx?Mi#jhMmgdM}81@!W<}foyNI#7k4Wq_1rOxBNuqzR$zpnm_riRZEb%=gyhM&?TMUb2c!NQZs{&y_pr2nivs^?g_#!3EB zV>CNhs26;wVZNw0nw`zwH(Xo)Zv2dyen>up`-9Q!MgFPdhooQXjb;a<{@I6q_^~k< z&0geRmHJ=$_5ZxKLjQf&wI|+G;QKM+$!K;kqSt+DYDS6E#O+T;vro;$_1tf`Uy+lG zy5AVh9@!5te|i&Z;yV~`WG#Fj>~?dSy}75@|GUbf^qMc8OWfC~YqTTtYj{c2ikL%TMa!%RU z@7#;g?8&*}@uS`Or;h%OW-rpqBI*WulKYjsn$zq>dfsbzevmvDc*8ZC9gNN&&l~RZ zB=>!uVMenT?fW(TAGv9_Iml73q!bJBm-9@TTd_W_veR(L;mcasIZNA$jrhx(28Gp>K$)5zZU zF<&3vfBDUl_g!P}`)=~U*5VKK<>UADRpPH-+FJafzI^-Y5z?w#%pA87Yb((IkZn19uc&SlT(X5X7L zntgOGrq{gj+-Cm#xwLx(?P!mVe|SRAdy@K*H*)g1S-YpNf9}uU(65lc-y#1$Lf#%l z|NE8gAFDlUkLrhilj;AaI=->xb4LHJ_N+asA32EiQ|isHA3yT_CHT4*`TsuVYdqtM z+B=ih?&mMmJKYJ@P4;R3$;Xsq2eap$i0P59apeEM;0@&ckI4U<%HfsFuI61r;`76Kv1AT@13-lH0-_s+%$1s28>SvSQ>XhFbZ=-%a{d#@4 zI=Q|23G<)TXNmf)ule&=p?(5=h58Nj73$B^TYY79a(nd`=quEpr^j8rbp33|f9d*7 ze(29%h58Bf70$0fU!ndyeWm)_q%T>2o?g$rlILHbuWMzh&s6S6%sr~|e`SUY5;m==%`U~_G>Mzh&s6S6%ss1+UOV*#KuT+15zI^>95Bc-Q zUo82Kio|cZ?)Mp^*+KvQ!aPNIYIrvw-$F+W`M0-S!xQB4x!_Re}ehqQ|8u34^T={|8wLPXs wyg$X?PvGZm^nX@4eNpYZ|FPcg`A@#*$6q1;p1xB4Jw4(rk$?REEJ-5&2M_dtS^xk5 literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au4.oas b/testdata/drc/drcSimpleTests_au4.oas deleted file mode 100644 index 3a6f5f206a560e7042bc91cf01caaa88e640e779..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1458 zcmd^8(Q6Y)9G=Ncb~l^El4h1*2v{o8Vo_@44wv@Ir8f0K)3j$2T6>ke5>z@Q~B>Q`YE5X zK3+|^LB1K@H2>>db8*Fx1C;gK!-{x0=)t~JGe$|m3tb7UlKSFj=K)%fwm1Ga(tY(8s zSrwODMNe(7-YxZfF6rU`02KdE(%FeZhykCtp&g;snIY>*H;u$p0QS#If z-DOidbk|aA^)yjdoF!p7uy}wd8fWw6cDsdP8n+zCoggZ;i}fi{GS@^CTFvB10RaFZ zV2+SEO1KA*xp~SS0?1O4Co9fsO5@y+D*7IFk_nAl9JmvxD@;^oX>M1p4G@)OFoTpU zk+-O+bv7z9xBgXR>Y-2)8o6z=C32$W{1XokEcXo8^mEM0J z-QWzg5W3D4(cQqS5pwRCKx;s|kJYDzv%*@?Op$g2(ekQ+Ot?*L?4`5Lb?PsnDi+XY zM>a#$0;-DIw{{DVBwHpSfop=QE7Jj$h&4=-(}XOg(P&-jV%34*)&@ D+5QXd diff --git a/testdata/drc/drcSimpleTests_au5.gds b/testdata/drc/drcSimpleTests_au5.gds new file mode 100644 index 0000000000000000000000000000000000000000..3578cc1c0ac95210d59cce0b0d7231e51abc3b8b GIT binary patch literal 1708 zcmbuQR+_jQk*+ z8Xp4EvBbdOQP1T4#cS{Ib^F=r$}DOUx+@IB`SIz|86chygv9|i5tal1T2rwo`WQ2d z8ENtXSlI(y-Flcoyj1hIQS{kpyy@3|6h-FP)vbRvYTjHlMKAr@2MTuSrEWc!FIgAQ z(v{!&(%ZRm|3KxXc$U64?MrXxs&9aJmcDtzm)_1*-!?0rrSH7*rMGj{_ne7m>HFip z^meZL!8h?N{pf-(y`8ImDkGkypF8)Zw{z7m9*bw`SKoc%oow{v|u;%{y;OS9Id*)^qvN1Mw_<#fvY!oh$bbRGa@C&tvK9yL{bxuKk+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>um*Yhx%)FiC|(AU>QsgehZrX?f0SyF z6sdW_b%Ui*a;G>G#|04)nS(q(xWKH+OoW;WkeUb2n0|1HROVq5W@;G8fPn!3akILa diff --git a/testdata/drc/drcSimpleTests_au6.gds b/testdata/drc/drcSimpleTests_au6.gds new file mode 100644 index 0000000000000000000000000000000000000000..75a2525f92b9902d417442aeb59fab4ecc708d74 GIT binary patch literal 1626 zcmbu9ze@sP7{?#a^VJRu6$vVd1PP)b=?76zXc%dQib<3NwKTMZLqkJ@LrYsjTZ>z3 zV}pZ3G&l4I2sJjiCermh_ulL4k*^UQ9G{o>`+nZ%o_mi22AZ}H5trtD12+sn2%<33 zJqDk~b~5V#VYg>3dA+p#eE+eWdYrA^mGThrs6B(RVj-E`1<<|FccqW9Om=S$bOXQ$ z35$)oDggd~8^Cn~bm>97`>ipWJ=Wk3rukJ??9fSt>jPke8k_(d($JY9`?GGEtr%zf z(lkvR3yS$xjMx69S9~jlOm;KJJMo2h=T?{26Ur_fnUQxqzatA*)!BGcolPaw*}U_O zJL;`A)LuHS&I%QEcK9N*o+psTV@co%AicP@bRj9`S%RNHFTdRY&}w*UNKY8YDM>M} z)l@##!oD!KNWTA;`$RF{&kt7kIwJYQ?~>%(x%Gwre13p2?3vaP$&W5biur!^Cnva1 zB!Bu!l6*T?){mFDPb7cwRg!!=SNbb;?i0yR#3aeLbEUr#;69Q3t#e88?Of^Sj=4`H zzxX6czMU)mg9q*t$v?tdkbFDu_D>yukiUPwU4CbOtB$BEsHlJT&-z{d-|FKZP}kub DD=q1k literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au6.oas b/testdata/drc/drcSimpleTests_au6.oas deleted file mode 100644 index 3b7878d85eae121c459bade432c3f79559ba1fdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 514 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>um*Yhx%)G-h6FkK1v9gF`h^)WL&SI) zco_v>GAv-@X80f~B$%;~jX|V3mEj534VFg9o#MPqf+rZcIWC9@zyL^g!y(2A%pav1 pBt>dK3J>!9;9>%*5vj~X$W(x29z0|E!6j0ehfSENVI%_v1^`Gf#ZLeL diff --git a/testdata/drc/drcSimpleTests_au7.gds b/testdata/drc/drcSimpleTests_au7.gds new file mode 100644 index 0000000000000000000000000000000000000000..3fa5aff17b83cbcb37c740b82b4f877d121ae996 GIT binary patch literal 3290 zcmbuBu}fP~6vj_plAH8lMMDs5rBn!lRH$gBC{$vxXljWPrBo5>;?SiyI5>33;Ns@c z&Be{tu|tLoBD7E_;vXQiV+U77`aI{|cfIdmZmj189^d1B_xH{_x!(x^1)lc-`chu* zH>BYK6rdk^{zEVrIsp=v?CI@YI z=9Q{c-;Y4X2k;AqQjbz~fR3&-Kx!B0lj+8*-5&qE#U6Y@5d7P4JmJPQY#u;8^x!+F zP7j$OqA!|3a9PrN^(6=byf%BPG7)k5%F$KTP=T_Xl{4 zJ<~o?;`?7%HS6u_4?NX8De=#?tr8#k&ica}nkOZG?9?jpk?-UunwlpizFe|OeB?X% zH(i=1CI0QERpKMx$+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>um*Yhx%)G-h6FkK1v9gF`h^)WL&SI) zco_v>GAv-@X80f~B$%;~jX|XPBqPHUt{W_kk~_tDnFLQTa&ue|5r6@(^oB!>6PQ0r zHAsrofK(pj`N72mR3uWFibaM&q%spBTmjPX;2F~oE|JPS?82uQ5#n(3fu@{A5=S=X N5>|1hhLH>y7yw1R>{tK* diff --git a/testdata/drc/drcSimpleTests_au8.gds b/testdata/drc/drcSimpleTests_au8.gds new file mode 100644 index 0000000000000000000000000000000000000000..86825b3c5a1030dd0e2d5e566d939dd96107e245 GIT binary patch literal 2792 zcmbtVJ!=$E6umpM^D@dR8pO{nQUwb^uvycH2{vLti>PaA4XB{;2gHD#f55^-un5># zX#{&eaAT(tENo&*6Im33$5?ZR+&?z}Vao_p@QLyCf+N%cxFvPmIr zqfr{CG50^kfto%&MYJc3PE6iyoO%7|`^2f|M;|WD&QLx2KTjz&NmQ*7sT~2mA+{0LCLY2m5poz68pzioQw{Dq6QcY1l>Yn}B@)V^~S?_1D1 z#YNzC+%s}~L3k%>SB76uyI852v*)_iLmji~9ZRc|o|10$(pn`Si4aMj*cx26{jc^_ zC?Z?qL($3cx%8Kvzs@o=_O9Uod}Ol<{%3i92i^Z)zZ2uo#UlIHvW_CyX37*oVJL6k(RNfwiXAzaHnv z@kq`x?4QmuUoQvX_w}-nU&716cNKU!H>8(>@8PB7m+;cgoEv%msQFX${D#F%;4|Da za(qMdlEX*Oo0-+HH~};>i;?4vKKvIrGx#ng2JyX0UOC8d`MtWC=Z_h=g#FBHG`I+S z;2_84%yjr(`W?R2%{o~(BgdtF$uF;eKG*M+zr**+U-HZ6e=E=5%Rh(jrQhLu>6iQx K`ti-JR_QM)tm19} literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au8.oas b/testdata/drc/drcSimpleTests_au8.oas deleted file mode 100644 index 869056430012c16bbd644d832c12c03c2209b5dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 656 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4jE>BVPRt-CD|19-6f1LRSU4|( za5cjTMh4BdEF#q>85w@?U*kO~@K;b+@g$=N<0(dwo0k}wwBNIcG+t&DX}ki&R~dO3 z1wS$T)qlw(Sg?j^1B-AqBhY}gVy#SIO}8&IGBB+dYi9y0WfJ_ra8aW{L+}G*3dpJy zhP7G~nT2g$Fxhf5Xv}6R60Tr8DcZ^;Qk}|hjrRxtNsEobA~hga?BqQQbX+AvDqI5t zOl@Tv7NsJ0^O)9aO=jk1 ex - end - assert_equal(ex.to_s, "Texts can only be identified on an original layer in Region::texts") + t = r.texts("*", true) + assert_equal(t.to_s, "") r.insert(RBA::Box::new(1, 2, 3, 4)) - ex = nil - begin - t = r.texts("*", true) - rescue => ex - end - assert_equal(ex.to_s, "Texts can only be identified on an original layer in Region::texts") + t = r.texts("*", true) + assert_equal(t.to_s, "") ly = RBA::Layout::new top = ly.create_cell("TOP") From 37cc84908ebfd3235077113b9a8e4552ad25ffdb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 9 Mar 2019 20:25:45 +0100 Subject: [PATCH 330/335] Updated test because of edge pair normlization --- src/db/unit_tests/dbEdgePairs.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/unit_tests/dbEdgePairs.cc b/src/db/unit_tests/dbEdgePairs.cc index 07583a268..5204360c7 100644 --- a/src/db/unit_tests/dbEdgePairs.cc +++ b/src/db/unit_tests/dbEdgePairs.cc @@ -147,5 +147,5 @@ TEST(4) ep.insert_into_as_polygons (&ly, top_cell, l1, 1); db::Region r (db::RecursiveShapeIterator (ly, ly.cell (top_cell), l1)); - EXPECT_EQ (r.to_string (), "(-11,-20;50,51;9,20;90,81);(-10,-21;9,20;110,121;91,80)"); + EXPECT_EQ (r.to_string (), "(-10,-21;9,20;50,51;91,80);(-10,-21;9,20;110,121;91,80)"); } From 5ea8c56db38971f4d0c4b00dc2108b43ad8e2222 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 9 Mar 2019 23:23:45 +0100 Subject: [PATCH 331/335] A minor doc fix. --- src/db/db/gsiDeclDbLayoutToNetlist.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 3de03e304..2ce0807eb 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -385,8 +385,8 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "@param cmap The mapping of internal layout to target layout for the circuit mapping\n" "@param target The target layout\n" "@param lmap Target layer indexes (keys) and net regions (values)\n" - "@param circuit_cell_name_prefix See method description\n" "@param net_cell_name_prefix See method description\n" + "@param circuit_cell_name_prefix See method description\n" "@param device_cell_name_prefix If given, devices will be output as separate cells\n" ) + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), From ab8107de2d62f5ffda2249a9edfb5733c926e9bf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Mar 2019 01:26:52 +0100 Subject: [PATCH 332/335] Bugfix: Spice writer needs 'P' suffix for source/drain area of MOS --- src/db/db/dbNetlistSpiceWriter.cc | 4 ++-- testdata/algo/nwriter5_au.txt | 4 ++-- testdata/algo/nwriter6_au.txt | 4 ++-- testdata/algo/nwriter8_au.txt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc index 0a06a87ab..e2c7586fa 100644 --- a/src/db/db/dbNetlistSpiceWriter.cc +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -147,8 +147,8 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const os << " L=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_L)); os << " W=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_W)); - os << " AS=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AS)); - os << " AD=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AD)); + os << " AS=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AS)); + os << " AD=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AD)); os << " PS=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PS)); os << " PD=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PD)); diff --git a/testdata/algo/nwriter5_au.txt b/testdata/algo/nwriter5_au.txt index 3c1b7df3c..123eb9eac 100644 --- a/testdata/algo/nwriter5_au.txt +++ b/testdata/algo/nwriter5_au.txt @@ -10,7 +10,7 @@ * net 3 n3 * net 4 n4 * device instance $1 0,0 M3CLS -M$1 1 4 3 1 MM3CLS L=0.25U W=0.18U AS=1.2U AD=0.75U PS=2.2U PD=1.75U +M$1 1 4 3 1 MM3CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U * device instance $2 0,0 M3CLS -M$2 3 4 2 3 MM3CLS L=1.4U W=0.25U AS=1.3U AD=0.85U PS=2.3U PD=1.85U +M$2 3 4 2 3 MM3CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U .ENDS C1 diff --git a/testdata/algo/nwriter6_au.txt b/testdata/algo/nwriter6_au.txt index 50133a5d9..33ef8c225 100644 --- a/testdata/algo/nwriter6_au.txt +++ b/testdata/algo/nwriter6_au.txt @@ -12,7 +12,7 @@ * net 4 n4 * net 5 n5 * device instance $1 0,0 M4CLS -M$1 1 4 3 5 MM4CLS L=0.25U W=0.18U AS=1.2U AD=0.75U PS=2.2U PD=1.75U +M$1 1 4 3 5 MM4CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U * device instance $2 0,0 M4CLS -M$2 3 4 2 5 MM4CLS L=1.4U W=0.25U AS=1.3U AD=0.85U PS=2.3U PD=1.85U +M$2 3 4 2 5 MM4CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U .ENDS C1 diff --git a/testdata/algo/nwriter8_au.txt b/testdata/algo/nwriter8_au.txt index 812ebff0f..7b3e360df 100644 --- a/testdata/algo/nwriter8_au.txt +++ b/testdata/algo/nwriter8_au.txt @@ -28,7 +28,7 @@ XSC2 3 2 4 3 C1 * net 4 n4 * net 5 n5 * device instance $1 0,0 M4CLS -M$1 1 4 3 5 MM4CLS L=0.25U W=0.18U AS=1.2U AD=0.75U PS=2.2U PD=1.75U +M$1 1 4 3 5 MM4CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U * device instance $2 0,0 M4CLS -M$2 3 4 2 5 MM4CLS L=1.4U W=0.25U AS=1.3U AD=0.85U PS=2.3U PD=1.85U +M$2 3 4 2 5 MM4CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U .ENDS C1 From 510c675d21b1df70c095e406f33fb6db26c0476d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Mar 2019 19:35:13 +0100 Subject: [PATCH 333/335] Test cases for DRC-based net extraction and flat extraction Flat extraction requires that texts of subcells are not considered. Otherwise they pollute the net namespace of the top cell. --- src/db/db/dbDeepShapeStore.cc | 13 +- src/db/db/dbDeepShapeStore.h | 6 +- src/db/db/dbLayoutToNetlist.cc | 2 +- src/db/unit_tests/dbDeepShapeStoreTests.cc | 6 +- src/drc/unit_tests/drcSimpleTests.cc | 120 ++++++++++++++++ testdata/drc/drcSimpleTests_10.drc | 97 +++++++++++++ testdata/drc/drcSimpleTests_9.drc | 99 ++++++++++++++ testdata/drc/drcSimpleTests_au10a.cir | 67 +++++++++ testdata/drc/drcSimpleTests_au10b.cir | 71 ++++++++++ testdata/drc/drcSimpleTests_au9a.cir | 151 +++++++++++++++++++++ testdata/drc/drcSimpleTests_au9b.cir | 83 +++++++++++ testdata/drc/ringo.gds | Bin 0 -> 9954 bytes 12 files changed, 707 insertions(+), 8 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_10.drc create mode 100644 testdata/drc/drcSimpleTests_9.drc create mode 100644 testdata/drc/drcSimpleTests_au10a.cir create mode 100644 testdata/drc/drcSimpleTests_au10b.cir create mode 100644 testdata/drc/drcSimpleTests_au9a.cir create mode 100644 testdata/drc/drcSimpleTests_au9b.cir create mode 100644 testdata/drc/ringo.gds diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index b2788f090..1e0c1e96b 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -296,7 +296,7 @@ DeepShapeStore::~DeepShapeStore () m_layouts.clear (); } -DeepLayer DeepShapeStore::create_from_flat (const db::Region ®ion, double max_area_ratio, size_t max_vertex_count, const db::ICplxTrans &trans) +DeepLayer DeepShapeStore::create_from_flat (const db::Region ®ion, bool for_netlist, double max_area_ratio, size_t max_vertex_count, const db::ICplxTrans &trans) { // reuse existing layer std::pair lff = layer_for_flat (region); @@ -322,12 +322,19 @@ DeepLayer DeepShapeStore::create_from_flat (const db::Region ®ion, double max db::PolygonReferenceHierarchyBuilderShapeReceiver refs (&layout (), m_text_enlargement, m_text_property_name); db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count); - // try to maintain the texts - go through shape iterator + // try to maintain the texts on top level - go through shape iterator std::pair ii = region.begin_iter (); db::ICplxTrans ttop = trans * ii.second; while (! ii.first.at_end ()) { - red.push (*ii.first, ttop * ii.first.trans (), world, 0, shapes); + + if (for_netlist && ii.first->is_text () && ii.first.layout () && ii.first.cell () != ii.first.top_cell ()) { + // Skip texts on levels below top cell. For the reasoning see the description of this method. + } else { + red.push (*ii.first, ttop * ii.first.trans (), world, 0, shapes); + } + ++ii.first; + } DeepLayer dl (this, 0 /*singular layout index*/, layer); diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 9c51a14a7..2246e8a18 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -267,8 +267,12 @@ public: * * After a flat layer has been created for a region, it can be retrieved * from the region later with layer_for_flat (region). + * + * If for_netlist is true, texts will be skipped except on top level. The + * reasoning is that texts below top level may create name clashes if they + * are used for net names. */ - DeepLayer create_from_flat (const db::Region ®ion, double max_area_ratio = 0.0, size_t max_vertex_count = 0, const db::ICplxTrans &trans = db::ICplxTrans ()); + DeepLayer create_from_flat (const db::Region ®ion, bool for_netlist, double max_area_ratio = 0.0, size_t max_vertex_count = 0, const db::ICplxTrans &trans = db::ICplxTrans ()); /** * @brief Gets the layer for a given flat region. diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 97cce0100..a0be5cfcd 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -313,7 +313,7 @@ void LayoutToNetlist::register_layer (const db::Region ®ion, const std::strin if (m_is_flat) { - dl = dss ().create_from_flat (region); + dl = dss ().create_from_flat (region, true); } else { diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc index 70d625495..9ef242efe 100644 --- a/src/db/unit_tests/dbDeepShapeStoreTests.cc +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -200,9 +200,9 @@ TEST(4_FlatAndEmptyInput) db::Region r3; - db::Region dr1 (new db::DeepRegion (dss.create_from_flat (r1))); - db::Region dr2 (new db::DeepRegion (dss.create_from_flat (r2))); - db::Region dr3 (new db::DeepRegion (dss.create_from_flat (r3))); + db::Region dr1 (new db::DeepRegion (dss.create_from_flat (r1, true))); + db::Region dr2 (new db::DeepRegion (dss.create_from_flat (r2, true))); + db::Region dr3 (new db::DeepRegion (dss.create_from_flat (r3, true))); EXPECT_EQ ((dr1 - dr2).to_string (), "(0,0;0,900;100,900;100,100;900,100;900,900;0,900;0,1000;1000,1000;1000,0)"); EXPECT_EQ ((dr1 - dr3).to_string (), "(0,0;0,1000;1000,1000;1000,0)"); diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index a8f69fb4f..e89defa34 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -24,6 +24,7 @@ #include "dbReader.h" #include "dbTestSupport.h" #include "lymMacro.h" +#include "tlFileUtils.h" TEST(1) { @@ -342,3 +343,122 @@ TEST(8_TextsAndPolygons) db::compare_layouts (_this, layout, au, db::NoNormalization); } +TEST(9_NetlistExtraction) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_9.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/ringo.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au9a.cir"; + + std::string au_simplified = tl::testsrc (); + au_simplified += "/testdata/drc/drcSimpleTests_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 ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + "$drc_test_target_simplified = '%s'\n" + , input, output, output_simplified) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + + // verify + + { + tl::InputStream is (output); + tl::InputStream is_au (au); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output), + tl::absolute_file_path (au))); + } + } + + { + tl::InputStream is (output_simplified); + tl::InputStream is_au (au_simplified); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output_simplified), + tl::absolute_file_path (au_simplified))); + } + } +} + +TEST(10_NetlistExtractionFlat) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_10.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/ringo.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au10a.cir"; + + std::string au_simplified = tl::testsrc (); + au_simplified += "/testdata/drc/drcSimpleTests_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 ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + "$drc_test_target_simplified = '%s'\n" + , input, output, output_simplified) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + + // verify + + { + tl::InputStream is (output); + tl::InputStream is_au (au); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output), + tl::absolute_file_path (au))); + } + } + + { + tl::InputStream is (output_simplified); + tl::InputStream is_au (au_simplified); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output_simplified), + tl::absolute_file_path (au_simplified))); + } + } +} diff --git a/testdata/drc/drcSimpleTests_10.drc b/testdata/drc/drcSimpleTests_10.drc new file mode 100644 index 000000000..83cf1ed6d --- /dev/null +++ b/testdata/drc/drcSimpleTests_10.drc @@ -0,0 +1,97 @@ +# Hierarchical extraction + +source($drc_test_source) + +# Drawing layers + +nwell = input(1, 0) +diff = input(2, 0) +pplus = input(3, 0) +nplus = input(4, 0) +poly = input(5, 0) +thickox = input(6, 0) +polyres = input(7, 0) +contact = input(8, 0) +metal1 = input(9, 0) +via = input(10, 0) +metal2 = input(11, 0) + +# Special layer for bulk terminals + +bulk = make_layer + +# Computed layers + +diff_in_nwell = diff & nwell +pdiff = diff_in_nwell - nplus +ntie = diff_in_nwell & nplus +pgate = pdiff & poly +psd = pdiff - pgate +hv_pgate = pgate & thickox +lv_pgate = pgate - hv_pgate +hv_psd = psd & thickox +lv_psd = psd - thickox + +diff_outside_nwell = diff - nwell +ndiff = diff_outside_nwell - pplus +ptie = diff_outside_nwell & pplus +ngate = ndiff & poly +nsd = ndiff - ngate +hv_ngate = ngate & thickox +lv_ngate = ngate - hv_ngate +hv_nsd = nsd & thickox +lv_nsd = nsd - thickox + +# PMOS transistor device extraction + +hvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVPMOS") +extract_devices(hvpmos_ex, { "SD" => psd, "G" => hv_pgate, "P" => poly, "W" => nwell }) + +lvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVPMOS") +extract_devices(lvpmos_ex, { "SD" => psd, "G" => lv_pgate, "P" => poly, "W" => nwell }) + +# NMOS transistor device extraction + +lvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVNMOS") +extract_devices(lvnmos_ex, { "SD" => nsd, "G" => lv_ngate, "P" => poly, "W" => bulk }) + +hvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVNMOS") +extract_devices(hvnmos_ex, { "SD" => nsd, "G" => hv_ngate, "P" => poly, "W" => bulk }) + + +# Define connectivity for netlist extraction + +# Inter-layer +connect(contact, ntie) +connect(contact, ptie) +connect(nwell, ntie) +connect(psd, contact) +connect(nsd, contact) +connect(poly, contact) +connect(contact, metal1) +connect(metal1, via) +connect(via, metal2) + +# Global connections +connect_global(ptie, "BULK") +connect_global(bulk, "BULK") + +# Actually performs the extraction + +netlist = l2n_data.netlist + +# Writes the netlist + +writer = RBA::NetlistSpiceWriter::new + +netlist.write($drc_test_target, writer, "RINGO netlist before simplification") + +# Netlist simplification + +netlist.combine_devices +netlist.make_top_level_pins +netlist.purge +netlist.purge_nets + +netlist.write($drc_test_target_simplified, writer, "RINGO netlist after simplification") + diff --git a/testdata/drc/drcSimpleTests_9.drc b/testdata/drc/drcSimpleTests_9.drc new file mode 100644 index 000000000..d033481be --- /dev/null +++ b/testdata/drc/drcSimpleTests_9.drc @@ -0,0 +1,99 @@ +# Hierarchical extraction + +source($drc_test_source) + +deep + +# Drawing layers + +nwell = input(1, 0) +diff = input(2, 0) +pplus = input(3, 0) +nplus = input(4, 0) +poly = input(5, 0) +thickox = input(6, 0) +polyres = input(7, 0) +contact = input(8, 0) +metal1 = input(9, 0) +via = input(10, 0) +metal2 = input(11, 0) + +# Special layer for bulk terminals + +bulk = make_layer + +# Computed layers + +diff_in_nwell = diff & nwell +pdiff = diff_in_nwell - nplus +ntie = diff_in_nwell & nplus +pgate = pdiff & poly +psd = pdiff - pgate +hv_pgate = pgate & thickox +lv_pgate = pgate - hv_pgate +hv_psd = psd & thickox +lv_psd = psd - thickox + +diff_outside_nwell = diff - nwell +ndiff = diff_outside_nwell - pplus +ptie = diff_outside_nwell & pplus +ngate = ndiff & poly +nsd = ndiff - ngate +hv_ngate = ngate & thickox +lv_ngate = ngate - hv_ngate +hv_nsd = nsd & thickox +lv_nsd = nsd - thickox + +# PMOS transistor device extraction + +hvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVPMOS") +extract_devices(hvpmos_ex, { "SD" => psd, "G" => hv_pgate, "P" => poly, "W" => nwell }) + +lvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVPMOS") +extract_devices(lvpmos_ex, { "SD" => psd, "G" => lv_pgate, "P" => poly, "W" => nwell }) + +# NMOS transistor device extraction + +lvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVNMOS") +extract_devices(lvnmos_ex, { "SD" => nsd, "G" => lv_ngate, "P" => poly, "W" => bulk }) + +hvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVNMOS") +extract_devices(hvnmos_ex, { "SD" => nsd, "G" => hv_ngate, "P" => poly, "W" => bulk }) + + +# Define connectivity for netlist extraction + +# Inter-layer +connect(contact, ntie) +connect(contact, ptie) +connect(nwell, ntie) +connect(psd, contact) +connect(nsd, contact) +connect(poly, contact) +connect(contact, metal1) +connect(metal1, via) +connect(via, metal2) + +# Global connections +connect_global(ptie, "BULK") +connect_global(bulk, "BULK") + +# Actually performs the extraction + +netlist = l2n_data.netlist + +# Writes the netlist + +writer = RBA::NetlistSpiceWriter::new + +netlist.write($drc_test_target, writer, "RINGO netlist before simplification") + +# Netlist simplification + +netlist.combine_devices +netlist.make_top_level_pins +netlist.purge +netlist.purge_nets + +netlist.write($drc_test_target_simplified, writer, "RINGO netlist after simplification") + diff --git a/testdata/drc/drcSimpleTests_au10a.cir b/testdata/drc/drcSimpleTests_au10a.cir new file mode 100644 index 000000000..2e308383c --- /dev/null +++ b/testdata/drc/drcSimpleTests_au10a.cir @@ -0,0 +1,67 @@ +* RINGO netlist before simplification + +* cell RINGO +.SUBCKT RINGO +* net 12 OUT +* net 27 ENABLE +* net 28 VDD +* net 29 FB +* net 43 BULK,VSS +* device instance $1 2.65,5.8 LVPMOS +M$1 2 27 28 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 3.35,5.8 LVPMOS +M$2 28 29 2 28 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 5.05,5.8 LVPMOS +M$3 28 2 3 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $4 6.85,5.8 LVPMOS +M$4 28 3 4 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $5 8.65,5.8 LVPMOS +M$5 28 4 5 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $6 10.45,5.8 LVPMOS +M$6 28 5 6 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $7 12.25,5.8 LVPMOS +M$7 28 6 7 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $8 14.05,5.8 LVPMOS +M$8 28 7 8 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $9 15.85,5.8 LVPMOS +M$9 28 8 9 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $10 17.65,5.8 LVPMOS +M$10 28 9 10 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $11 19.45,5.8 LVPMOS +M$11 28 10 11 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $12 21.25,5.8 LVPMOS +M$12 28 11 29 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $13 23.05,5.8 LVPMOS +M$13 28 29 12 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $14 2.65,2.135 LVNMOS +M$14 43 27 14 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U ++ PD=1.4U +* device instance $15 3.35,2.135 LVNMOS +M$15 14 29 2 43 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +* device instance $16 5.05,2.135 LVNMOS +M$16 43 2 3 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $17 6.85,2.135 LVNMOS +M$17 43 3 4 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $18 8.65,2.135 LVNMOS +M$18 43 4 5 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $19 10.45,2.135 LVNMOS +M$19 43 5 6 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $20 12.25,2.135 LVNMOS +M$20 43 6 7 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $21 14.05,2.135 LVNMOS +M$21 43 7 8 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $22 15.85,2.135 LVNMOS +M$22 43 8 9 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $23 17.65,2.135 LVNMOS +M$23 43 9 10 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +* device instance $24 19.45,2.135 LVNMOS +M$24 43 10 11 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +* device instance $25 21.25,2.135 LVNMOS +M$25 43 11 29 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +* device instance $26 23.05,2.135 LVNMOS +M$26 43 29 12 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +.ENDS RINGO diff --git a/testdata/drc/drcSimpleTests_au10b.cir b/testdata/drc/drcSimpleTests_au10b.cir new file mode 100644 index 000000000..dec252d6a --- /dev/null +++ b/testdata/drc/drcSimpleTests_au10b.cir @@ -0,0 +1,71 @@ +* RINGO netlist after simplification + +* cell RINGO +* pin OUT +* pin ENABLE +* pin VDD +* pin FB +* pin BULK,VSS +.SUBCKT RINGO 11 13 14 15 16 +* net 11 OUT +* net 13 ENABLE +* net 14 VDD +* net 15 FB +* net 16 BULK,VSS +* device instance $1 2.65,5.8 LVPMOS +M$1 1 13 14 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 3.35,5.8 LVPMOS +M$2 14 15 1 14 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 5.05,5.8 LVPMOS +M$3 14 1 2 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $4 6.85,5.8 LVPMOS +M$4 14 2 3 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $5 8.65,5.8 LVPMOS +M$5 14 3 4 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $6 10.45,5.8 LVPMOS +M$6 14 4 5 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $7 12.25,5.8 LVPMOS +M$7 14 5 6 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $8 14.05,5.8 LVPMOS +M$8 14 6 7 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $9 15.85,5.8 LVPMOS +M$9 14 7 8 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $10 17.65,5.8 LVPMOS +M$10 14 8 9 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $11 19.45,5.8 LVPMOS +M$11 14 9 10 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $12 21.25,5.8 LVPMOS +M$12 14 10 15 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $13 23.05,5.8 LVPMOS +M$13 14 15 11 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $14 2.65,2.135 LVNMOS +M$14 16 13 12 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U ++ PD=1.4U +* device instance $15 3.35,2.135 LVNMOS +M$15 12 15 1 16 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +* device instance $16 5.05,2.135 LVNMOS +M$16 16 1 2 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $17 6.85,2.135 LVNMOS +M$17 16 2 3 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $18 8.65,2.135 LVNMOS +M$18 16 3 4 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $19 10.45,2.135 LVNMOS +M$19 16 4 5 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $20 12.25,2.135 LVNMOS +M$20 16 5 6 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $21 14.05,2.135 LVNMOS +M$21 16 6 7 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $22 15.85,2.135 LVNMOS +M$22 16 7 8 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $23 17.65,2.135 LVNMOS +M$23 16 8 9 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $24 19.45,2.135 LVNMOS +M$24 16 9 10 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +* device instance $25 21.25,2.135 LVNMOS +M$25 16 10 15 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +* device instance $26 23.05,2.135 LVNMOS +M$26 16 15 11 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +.ENDS RINGO diff --git a/testdata/drc/drcSimpleTests_au9a.cir b/testdata/drc/drcSimpleTests_au9a.cir new file mode 100644 index 000000000..e62b44d8e --- /dev/null +++ b/testdata/drc/drcSimpleTests_au9a.cir @@ -0,0 +1,151 @@ +* RINGO netlist before simplification + +* cell RINGO +.SUBCKT RINGO +* net 11 FB +* net 12 VDD +* net 15 OUT +* net 16 ENABLE +* net 19 BULK,VSS +* cell instance $1 r0 *1 1.8,0 +X$1 12 1 19 12 11 16 19 ND2X1 +* cell instance $2 r0 *1 4.2,0 +X$2 12 2 19 12 1 19 INVX1 +* cell instance $3 r0 *1 6,0 +X$3 12 3 19 12 2 19 INVX1 +* cell instance $4 r0 *1 7.8,0 +X$4 12 4 19 12 3 19 INVX1 +* cell instance $5 r0 *1 9.6,0 +X$5 12 5 19 12 4 19 INVX1 +* cell instance $6 r0 *1 11.4,0 +X$6 12 6 19 12 5 19 INVX1 +* cell instance $7 r0 *1 13.2,0 +X$7 12 7 19 12 6 19 INVX1 +* cell instance $8 r0 *1 15,0 +X$8 12 8 19 12 7 19 INVX1 +* cell instance $9 r0 *1 16.8,0 +X$9 12 9 19 12 8 19 INVX1 +* cell instance $10 r0 *1 18.6,0 +X$10 12 10 19 12 9 19 INVX1 +* cell instance $11 r0 *1 20.4,0 +X$11 12 11 19 12 10 19 INVX1 +* cell instance $12 r0 *1 22.2,0 +X$12 12 15 19 12 11 19 INVX1 +* cell instance $13 r0 *1 3.28,4 +X$13 11 M1M2 +* cell instance $14 r0 *1 21.42,4 +X$14 11 M1M2 +* cell instance $15 r0 *1 0.6,0 +X$15 12 19 TIE +* cell instance $16 r0 *1 0,0 +X$16 12 19 12 EMPTY +* cell instance $17 r0 *1 24,0 +X$17 12 19 TIE +* cell instance $18 r0 *1 25.2,0 +X$18 12 19 12 EMPTY +* cell instance $19 r0 *1 23.6,4 +X$19 15 M1M2 +* cell instance $20 r0 *1 2.6,3.1 +X$20 16 M1M2 +.ENDS RINGO + +* cell ND2X1 +* pin VDD +* pin OUT +* pin VSS +* pin +* pin B +* pin A +* pin BULK +.SUBCKT ND2X1 1 2 3 4 5 6 9 +* net 1 VDD +* net 2 OUT +* net 3 VSS +* net 5 B +* net 6 A +* net 9 BULK +* cell instance $1 r0 *1 0.3,5.05 +X$1 6 1 2 PMOS3 +* cell instance $2 r0 *1 1,5.05 +X$2 5 2 1 PMOS3 +* cell instance $3 r0 *1 1,1.66 +X$3 5 2 10 NMOS2 +* cell instance $4 r0 *1 0.3,1.66 +X$4 6 10 3 NMOS2 +* cell instance $5 r0 *1 1.48,4 +X$5 5 POLYM1 +* cell instance $6 r0 *1 0.8,3.1 +X$6 6 POLYM1 +* device instance $1 0.85,5.8 LVPMOS +M$1 2 6 1 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 1.55,5.8 LVPMOS +M$2 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 0.85,2.135 LVNMOS +M$3 3 6 10 9 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U +* device instance $4 1.55,2.135 LVNMOS +M$4 10 5 2 9 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +.ENDS ND2X1 + +* cell INVX1 +* pin VDD +* pin OUT +* pin VSS +* pin +* pin IN +* pin BULK +.SUBCKT INVX1 1 2 3 4 5 7 +* net 1 VDD +* net 2 OUT +* net 3 VSS +* net 5 IN +* net 7 BULK +* cell instance $1 r0 *1 0.3,5.05 +X$1 5 2 1 PMOS3 +* cell instance $2 r0 *1 0.3,1.66 +X$2 5 2 3 NMOS2 +* cell instance $3 r0 *1 0.6,3.1 +X$3 5 POLYM1 +* device instance $1 0.85,5.8 LVPMOS +M$1 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $2 0.85,2.135 LVNMOS +M$2 3 5 2 7 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +.ENDS INVX1 + +* cell M1M2 +* pin +.SUBCKT M1M2 1 +.ENDS M1M2 + +* cell TIE +* pin VDD +* pin BULK,VSS +.SUBCKT TIE 2 3 +* net 2 VDD +* net 3 BULK,VSS +.ENDS TIE + +* cell EMPTY +* pin +* pin +* pin +.SUBCKT EMPTY 1 2 3 +.ENDS EMPTY + +* cell POLYM1 +* pin +.SUBCKT POLYM1 1 +.ENDS POLYM1 + +* cell NMOS2 +* pin +* pin +* pin +.SUBCKT NMOS2 1 2 3 +.ENDS NMOS2 + +* cell PMOS3 +* pin +* pin +* pin +.SUBCKT PMOS3 1 2 3 +.ENDS PMOS3 diff --git a/testdata/drc/drcSimpleTests_au9b.cir b/testdata/drc/drcSimpleTests_au9b.cir new file mode 100644 index 000000000..32e7dfc09 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au9b.cir @@ -0,0 +1,83 @@ +* RINGO netlist after simplification + +* cell RINGO +* pin FB +* pin VDD +* pin OUT +* pin ENABLE +* pin BULK,VSS +.SUBCKT RINGO 11 12 13 14 15 +* net 11 FB +* net 12 VDD +* net 13 OUT +* net 14 ENABLE +* net 15 BULK,VSS +* cell instance $1 r0 *1 1.8,0 +X$1 12 1 15 12 11 14 15 ND2X1 +* cell instance $2 r0 *1 4.2,0 +X$2 12 2 15 12 1 15 INVX1 +* cell instance $3 r0 *1 6,0 +X$3 12 3 15 12 2 15 INVX1 +* cell instance $4 r0 *1 7.8,0 +X$4 12 4 15 12 3 15 INVX1 +* cell instance $5 r0 *1 9.6,0 +X$5 12 5 15 12 4 15 INVX1 +* cell instance $6 r0 *1 11.4,0 +X$6 12 6 15 12 5 15 INVX1 +* cell instance $7 r0 *1 13.2,0 +X$7 12 7 15 12 6 15 INVX1 +* cell instance $8 r0 *1 15,0 +X$8 12 8 15 12 7 15 INVX1 +* cell instance $9 r0 *1 16.8,0 +X$9 12 9 15 12 8 15 INVX1 +* cell instance $10 r0 *1 18.6,0 +X$10 12 10 15 12 9 15 INVX1 +* cell instance $11 r0 *1 20.4,0 +X$11 12 11 15 12 10 15 INVX1 +* cell instance $12 r0 *1 22.2,0 +X$12 12 13 15 12 11 15 INVX1 +.ENDS RINGO + +* cell ND2X1 +* pin VDD +* pin OUT +* pin VSS +* pin +* pin B +* pin A +* pin BULK +.SUBCKT ND2X1 1 2 3 4 5 6 7 +* net 1 VDD +* net 2 OUT +* net 3 VSS +* net 5 B +* net 6 A +* net 7 BULK +* device instance $1 0.85,5.8 LVPMOS +M$1 2 6 1 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 1.55,5.8 LVPMOS +M$2 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 0.85,2.135 LVNMOS +M$3 3 6 8 7 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U +* device instance $4 1.55,2.135 LVNMOS +M$4 8 5 2 7 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +.ENDS ND2X1 + +* cell INVX1 +* pin VDD +* pin OUT +* pin VSS +* pin +* pin IN +* pin BULK +.SUBCKT INVX1 1 2 3 4 5 6 +* net 1 VDD +* net 2 OUT +* net 3 VSS +* net 5 IN +* net 6 BULK +* device instance $1 0.85,5.8 LVPMOS +M$1 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $2 0.85,2.135 LVNMOS +M$2 3 5 2 6 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +.ENDS INVX1 diff --git a/testdata/drc/ringo.gds b/testdata/drc/ringo.gds new file mode 100644 index 0000000000000000000000000000000000000000..fa116f14dc2079afc0090b1f54566677f25024e3 GIT binary patch literal 9954 zcmd^^Ux;1R6~_1dckZ3Z#F~ya7R(#4%jd_eN_Wuvu-=FY(IzDBmCj3akSLlD+4VtON zg<~i7bVnM`6h`(h^dzI=f>)iZzbKX6Yr#z6`WqM>Zcc5S1G_y}*`b9zuhB`qcE)(B z>?!Z+`Ap%O=xDqS|3|?GjhbTr*AxCX==*tG32pog{=bu}?9fu4EKjoWDLb@~Z|n}f zq2BkkT*-&_Ql9-SIJJmXcuzh#F;<4^hj z$4Y6wiRi{STId5;id1%Jp?~&~*HOu_-%{CA9yzZ&|H*2C8TZ0UGfmpO{{btllVuBY z3)od{oyBpQsF--Jf7D z$EWO6;|I(Ws)WUZdrr<_vPYd8om9;*HqpTI-|5`gjNPZdwFS@Pt8r_{qMohI;@SN^ zZVfwMw{>%fq;O~37tQZ==hD{r&WQQ$ojZM%4I=KZ&TW8q>f}XS@TXj5helDH?xOYo z8BZnO_;0S{L%Ziky#H==W#6vjX>`UhceTH`Db68}&a=*qob_6P!pwV0PhdD-cp{$C zlNpZM{9U7pBpF6braq_RUx`6+U^&lGY4`L4>o ztDN7mX#7?@CGt|)p@sfy&sqPe>~|galCuAyy_BaPUPr&=rLvd#Z?0gx@aq-hZ(-SR z{9a`*89)7!_X>{w$$OQ(gr{GQlge?^uT=K3=SPm?q;lNkrLvcepU*w})Sh4af;~T} z?9kHr=XD%EIgVQ@dl^svBNOI-4s;pv+Ty66?77R*S^4>6?F4wNw0L*S*>F1RQ}+*%?NfWsqD~rR{Qwvf!`5$ zsqD}~o>eOP=cv=jgdIeNRQ9s|*UlJEC0GAruH-{|p?|)M@j5E|+d7=9>}CGh@5zhS ze_qf2OJ#=^`e*+*-_%h&1@>PmJJj=IaD%sa%>Nd6aoN@y^Vaqn{vNxt^}xOU*z0@- zZGM1P-kq*9ovj=^2p4>dugW)!KXGCmd}pt(4dDOSBg^aH;hU95Hl6KX2hDee@m10X z0^PB>+n85Tn6>D*>j8^>Y19-yyIRis3e9agZ|XU$YN_m-!h7-HDr|mnrJxGK_o1FD zEUt!zud~7&L|=grZ(mET(}pY5Jbvbet3O!lkg8tmkixUB;ES~msiA6}ZVks8hZHo9 zh@{5h-mY3ewSiRjvK&dCxsa;bKq`A#j%05ACf5iYKXW;?eZguBsqAGrp7YM@sN|~F z2=vPw?-jcNYaHfyud;_6k5J-n-WNZ^)(C;`%f`yu%pQzasq9sM+@yS&Z3_ zy54Nes)pH^JhQP>c4)l*lJV28vL>d!G@IUP{?uFoJFc9XC>cE8Pi+ewb;y=TvfzWIThHI~}G0RCLAyk}^S{YPKtt$phYraL|`UMf2@=F>g*CN|FL|l#(768z zAOCjQ{xSN01MwF$YAfZpWf;R-@cXp!QrV%NAG4ef|AXJ)n0sdI4QyR+aO4}tci-T` z^Do`CIe*-BsnHRSzjW7bO&L$ExNGBE^r400%ctOcxLuL^Hgj9`eLH@q8qU}Fs`-NY zxfqV(zD-}%_ia)=`*sqyd^TDid{(?(Dtnc6L!Rq~TCi?P!JsUtaY0j_#Sag;`S@euoJw!IO_ZnUFoL)?r7x#_JZfNm zdF|$1%N0();l|(d!>@!JpY^NFEq#=)y3lJ-gR6zGT9bFQDty={MyInwi~A28J(Scd VL-k Date: Sun, 10 Mar 2019 22:37:32 +0100 Subject: [PATCH 334/335] Custom devices for device extractor - tests in the DRC framework --- src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 6 +- src/drc/unit_tests/drcSimpleTests.cc | 60 ++++++ testdata/drc/drcSimpleTests_10.drc | 2 +- testdata/drc/drcSimpleTests_11.drc | 204 +++++++++++++++++++ testdata/drc/drcSimpleTests_au11a.cir | 17 ++ testdata/drc/drcSimpleTests_au11b.cir | 19 ++ testdata/drc/vdiv.gds | Bin 0 -> 4288 bytes 7 files changed, 304 insertions(+), 4 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_11.drc create mode 100644 testdata/drc/drcSimpleTests_au11a.cir create mode 100644 testdata/drc/drcSimpleTests_au11b.cir create mode 100644 testdata/drc/vdiv.gds diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index 29c04a5eb..911e40aed 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -100,9 +100,9 @@ namespace tl template<> struct type_traits : public tl::type_traits { - // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor + // mark "NetlistDeviceExtractor" as having a default ctor and no copy ctor typedef tl::false_tag has_copy_constructor; - typedef tl::false_tag has_default_constructor; + typedef tl::true_tag has_default_constructor; }; } @@ -212,7 +212,7 @@ Class decl_dbNetlistDeviceExtractor ("db", "DeviceEx "@brief Iterates over all errors collected in the device extractor." ), "@brief The base class for all device extractors.\n" - "This is an abstract base class for device extractors. See \\NetlistDeviceExtractor for a generic " + "This is an abstract base class for device extractors. See \\GenericDeviceExtractor for a generic " "class which you can reimplement to supply your own customized device extractor. " "In many cases using one of the preconfigured specific device extractors may be useful already and " "it's not required to implement a custom one. For an example about a preconfigured device extractor see " diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index e89defa34..885495d8c 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -438,6 +438,66 @@ TEST(10_NetlistExtractionFlat) EXPECT_EQ (drc.run (), 0); + // verify + + { + tl::InputStream is (output); + tl::InputStream is_au (au); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output), + tl::absolute_file_path (au))); + } + } + + { + tl::InputStream is (output_simplified); + tl::InputStream is_au (au_simplified); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output_simplified), + tl::absolute_file_path (au_simplified))); + } + } +} + +TEST(11_CustomDevices) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_11.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/vdiv.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au11a.cir"; + + std::string au_simplified = tl::testsrc (); + au_simplified += "/testdata/drc/drcSimpleTests_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 ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + "$drc_test_target_simplified = '%s'\n" + , input, output, output_simplified) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + // verify { diff --git a/testdata/drc/drcSimpleTests_10.drc b/testdata/drc/drcSimpleTests_10.drc index 83cf1ed6d..743acbd8c 100644 --- a/testdata/drc/drcSimpleTests_10.drc +++ b/testdata/drc/drcSimpleTests_10.drc @@ -1,4 +1,4 @@ -# Hierarchical extraction +# Flat extraction source($drc_test_source) diff --git a/testdata/drc/drcSimpleTests_11.drc b/testdata/drc/drcSimpleTests_11.drc new file mode 100644 index 000000000..e9a059603 --- /dev/null +++ b/testdata/drc/drcSimpleTests_11.drc @@ -0,0 +1,204 @@ + +source($drc_test_source) + +# Drawing layers + +nwell = input(1, 0) +diff = input(2, 0) +pplus = input(3, 0) +nplus = input(4, 0) +poly = input(5, 0) +thickox = input(6, 0) +polyres = input(7, 0) +contact = input(8, 0) +metal1 = input(9, 0) +via = input(10, 0) +metal2 = input(11, 0) + +# Special layer for bulk terminals + +bulk = make_layer + +# Computed layers + +poly_not_res = poly - polyres +poly_in_res = poly & polyres + +diff_in_nwell = diff & nwell +pdiff = diff_in_nwell - nplus +ntie = diff_in_nwell & nplus +pgate = pdiff & poly_not_res +psd = pdiff - pgate +hv_pgate = pgate & thickox +lv_pgate = pgate - hv_pgate +hv_psd = psd & thickox +lv_psd = psd - thickox + +diff_outside_nwell = diff - nwell +ndiff = diff_outside_nwell - pplus +ptie = diff_outside_nwell & pplus +ngate = ndiff & poly_not_res +nsd = ndiff - ngate +hv_ngate = ngate & thickox +lv_ngate = ngate - hv_ngate +hv_nsd = nsd & thickox +lv_nsd = nsd - thickox + +# Resistor device extraction + +class CustomResistorExtraction < RBA::GenericDeviceExtractor + + def initialize(name, sheet_rho) + self.name = name + @sheet_rho = sheet_rho + end + + def setup + + define_layer("M", "Marker") + define_layer("C", "Conductor") + define_layer("R", "Resistor") + + register_device_class (RBA::DeviceClassResistor::new); + + end + + def extract_devices(layer_geometry) + + marker = layer_geometry[0] + conductor = layer_geometry[1] + resistor = layer_geometry[2] + + conductor_geometry_index = 1 + + resistor_merged = resistor.merged + + marker_edges = marker.merged.edges & resistor_merged.edges + + resistor_merged.each do |r| + + connection_edges = marker_edges.interacting(RBA::Region::new(r)) + + terminals = connection_edges.extended_out(1) + + if terminals.size != 2 + error("Resistor shape does not touch marker border in exactly two places", r) + else + + # A = L*W + # P = 2*(L+W) + # -> L = p+sqrt(p*p-A) + # -> W = p-sqrt(p*p-A) + # (p=P/4) + + p = 0.25 * r.perimeter + a = r.area + + d = Math.sqrt(p * p - a) + l = p + d + w = p - d + + if w > 1e-3 + + device = create_device + + device.set_parameter(RBA::DeviceClassResistor::PARAM_R, @sheet_rho * l / w); + + define_terminal(device, RBA::DeviceClassResistor::TERMINAL_A, conductor_geometry_index, terminals[0]); + define_terminal(device, RBA::DeviceClassResistor::TERMINAL_B, conductor_geometry_index, terminals[1]); + + end + + end + + end + + end + + def get_connectivity(layout, layers) + + # the layer definition is marker, conductor, resistor + # * resistor is used for extraction + # * conductor is used for producing the terminals + # * marker is included because it's required for the extraction + + marker = layers[0] + conductor = layers[1] + resistor = layers[2] + + conn = RBA::Connectivity::new + + conn.connect(resistor, resistor) + conn.connect(conductor, resistor) + conn.connect(marker, resistor) + + return conn; + + end + +end + + + +# Resistor + +# Assumes a sheet rho of 150 Ohm/Square +res_ex = CustomResistorExtraction::new("RES", 150.0) +extract_devices(res_ex, { "C" => poly_not_res, "R" => poly_in_res, "M" => polyres }) + +# PMOS transistor device extraction + +hvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVPMOS") +extract_devices(hvpmos_ex, { "SD" => psd, "G" => hv_pgate, "P" => poly_not_res, "W" => nwell }) + +lvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVPMOS") +extract_devices(lvpmos_ex, { "SD" => psd, "G" => lv_pgate, "P" => poly_not_res, "W" => nwell }) + +# NMOS transistor device extraction + +lvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVNMOS") +extract_devices(lvnmos_ex, { "SD" => nsd, "G" => lv_ngate, "P" => poly_not_res, "W" => bulk }) + +hvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVNMOS") +extract_devices(hvnmos_ex, { "SD" => nsd, "G" => hv_ngate, "P" => poly_not_res, "W" => bulk }) + + +# Define connectivity for netlist extraction + +# Inter-layer +connect(contact, ntie) +connect(contact, ptie) +connect(nwell, ntie) +connect(psd, contact) +connect(nsd, contact) +connect(poly_not_res, contact) +connect(contact, metal1) +connect(metal1, via) +connect(via, metal2) + +# Global connections +connect_global(ptie, "BULK") +connect_global(bulk, "BULK") + +# Actually performs the extraction + +netlist = l2n_data.netlist + +# Write the netlist + +writer = RBA::NetlistSpiceWriter::new + +writer = RBA::NetlistSpiceWriter::new +netlist.write($drc_test_target, writer, "VDIV netlist before simplification") + +# Netlist simplification + +# NOTE: use make_top_level_pins before combine_devices as the pin +# stops the three resistors to be combined into one +netlist.make_top_level_pins +netlist.combine_devices +netlist.purge +netlist.purge_nets + +netlist.write($drc_test_target_simplified, writer, "VDIV netlist after simplification") + diff --git a/testdata/drc/drcSimpleTests_au11a.cir b/testdata/drc/drcSimpleTests_au11a.cir new file mode 100644 index 000000000..8b06b1937 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au11a.cir @@ -0,0 +1,17 @@ +* VDIV netlist before simplification + +* cell TOP +.SUBCKT TOP +* net 1 OUT +* net 2 GND +* net 4 IN +* net 7 VDD +* device instance $1 1.025,0.335 RES +R$1 6 1 7650 +* device instance $2 2.85,0.335 RES +R$2 3 1 7650 +* device instance $3 4.665,0.335 RES +R$3 3 2 2670 +* device instance $4 1.765,7.485 HVPMOS +M$4 6 4 7 7 MHVPMOS L=0.25U W=1.5U AS=0.63P AD=0.63P PS=3.84U PD=3.84U +.ENDS TOP diff --git a/testdata/drc/drcSimpleTests_au11b.cir b/testdata/drc/drcSimpleTests_au11b.cir new file mode 100644 index 000000000..a98b9510e --- /dev/null +++ b/testdata/drc/drcSimpleTests_au11b.cir @@ -0,0 +1,19 @@ +* VDIV netlist after simplification + +* cell TOP +* pin OUT +* pin GND +* pin IN +* pin VDD +.SUBCKT TOP 1 2 3 5 +* net 1 OUT +* net 2 GND +* net 3 IN +* net 5 VDD +* device instance $1 1.025,0.335 RES +R$1 4 1 7650 +* device instance $2 2.85,0.335 RES +R$2 2 1 10320 +* device instance $4 1.765,7.485 HVPMOS +M$4 4 3 5 5 MHVPMOS L=0.25U W=1.5U AS=0.63P AD=0.63P PS=3.84U PD=3.84U +.ENDS TOP diff --git a/testdata/drc/vdiv.gds b/testdata/drc/vdiv.gds new file mode 100644 index 0000000000000000000000000000000000000000..48f2d16f9d3d864bdac06e644e602c4aaae6d27c GIT binary patch literal 4288 zcmcJSO>7la6vxke-I+d${oqkse5RoV`ZVR!Di943EU8%NBT$H7VvHd%5L`%Hm}m?O z6LqH>Bq1TJKw>n;n5YXk#JFIiF>Fbi_>mY0VSx)-^zlD)-g$GbW?D+iB=dglnfsq} z@45ebXNDYw;mcGD!t{5Fs6u_zMH}tkl!b2Zo?S%MDA}>|t=>XZCR zjf16nzCX8PvX1jBH9nu-eI@!7_hG$K<6voi?mxFfpu=dT-@srG<(p<9o(CULv(Dyqy!Sq+;|E-|L!KKtkUj8Fey8z5Wc(J2kzH`cT zn}4x-=j9)fu?Jv=z0^HhFt3eV&-2f-Q)kW{*nJRFh9TG|np=%=QPvgV_b!cfgTXVX z^gl#B-xZ90!On9DXN$pQJXY&yd)ceLw|nypCtLUzDNo6Ll^O?a^*lY_pZ=0O?6#G! ziW`|42d&=Qy=f=%zk{}*8wD{YrN+Thef2o{3|-C{Xjq>Als5w+2eKA}{FP zgm!a5je|D7r?0Mu_6G8U;cDcE+mspytv>bKGYdVAeD}ucUGbW%UM3$L$C2yr%RQK@ zIo(rL_uQ{LxVn6VC(#?YPN{Lwj&8}_5x|w~4t5v2L#c7l=J(vS6z*V0usgyx#2reF zgEs#|b4T=nxI>9vEzlj)js0*^1v^Qv>@#jvaGI#Imh9p;Ts}J49dUp=H)K?3UFmhL z_s63Bx6)kNSI!Xizhc|B-potd^RC>#q21o}hI>#;Ug(~Y+vmMmHbm6-<$qdlq4vG~ z)+>K3TF?~GYk&?ivgmxN{xdyzo)O@{W_S2 zCHOu4G$!lmf*J=)^(k@>ccK4a{X_KsT0xD2rTU(CvBSQHryj`tUGaWFjf16n&O3NV z@^e1Uuhcl0>pNxsG5)>~c65&h{Drv+YlAu;OP+r!KgMJQRNxAy;Py-{Jo~bjFsn11y>SSWYpdtIDe{-%&>-F~z$Z7s1z7h= zje}P2y(y*`+R$}WAFTgC)UmCg#zCu(?XxoNh5ifZ!TJs8PZZQRX!XmjUN#l^UH>Uo Q{xKq@xt8nktc_#32R04HC;$Ke literal 0 HcmV?d00001 From 15040101ec664c1369c44c488f79774f6e7d21ab Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Mar 2019 22:38:27 +0100 Subject: [PATCH 335/335] Fixed a small issue in the adjust cell origin dialog. --- src/laybasic/laybasic/layDialogs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/laybasic/laybasic/layDialogs.h b/src/laybasic/laybasic/layDialogs.h index 243923fa7..04ad110b1 100644 --- a/src/laybasic/laybasic/layDialogs.h +++ b/src/laybasic/laybasic/layDialogs.h @@ -370,9 +370,10 @@ public: bool exec_dialog (int &mode_x, int &mode_y, bool &visible_only, bool &adjust_calls); -private: +private slots: void button_clicked (); +private: Ui::AlignCellOptionsDialog *mp_ui; };

    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%0A0m;y2`#ZN1dr*FNX_ z$RX`=Z&s;x7fwOlzud#be+(P!zW8Ym}i9KWgveEv9#HHn-_UYCOwU4(v)V|7kq4r&thx(pi z{ZReEF8XCEKF%21;rKAMFF0e-9`DCZ?HgPkYG2{?o|{rnlfzSaIU>E~}L9z*JFOnd;Y>%f{S4Q~is) zOP~Io`P^gfAEbY0zF6ZOs$XZm@PPYgs$Z6e>X*wy^~>^5{j$6z{X$-;{-ymc`{>^} zFB!Ohruuiz4JX_`Q~is)OMi>>`7T9%GBv-JoVDKb3o^ebuhhJ8d3e9AMINeszAF$v zu-2>kyV}S8C8~?>DG-}hzoFZ}0yZU@rth2Pm$N&As^wZBXHOJ62C)jp)% zrGKzd1c$~sQxYe%$Uam+P*Iytt1~t z-qpXW)W2mveV|#g^z1_U*xz?U`P%YOb}xI-4$nU`dSX}o_Eky{;1f zM&7Ic1g)QnvkB4q|7%la%3+7T*T2b6N?&nc>T~=rIiH90Yx4hS+*@MLU?hnu-f^|VYm?9lh` z6Po#Tjz3fWYc5`G|3Uo9MR2A3?($IMVtJ@>vpm$eQZMVbsd2YFR9v_`l>aRc6<3yr zio52rla4==b{?|F@nR~Dn_tj4go<;QhuDdJNI%dIHIJ;{lKF-_v>(vNFCuJ>@k!E2%f~E`8S9Q+}xS z)ec1e)cfomQvEySC#pA;{g#J{6U#%zk>#P{%<`7_7kOpdZr}K@o}F^!sQm%mzoYvq z@l)g-{quY}^?r&MwPVu$19p`5MdIevHQL{YiaX@QpQ*UCJXCzUJXF7^m-%GUZ}da? z#pNM>LmtYnmWTNNAsdSSA?^H<@<*t7bn4>|c|Mt%N0x`!iC#!Q&<{0_tk;tGKpxr; z=;IF&|DO85l26U}QS9({!+bjRhj$czL;U^U`-jv#y3ZLKJ)a=+=+qzY9TGqM;E3bR z#2?5*&8PdEv(EF$lppW2WVQVX=?CSN*iZW*_P6$R*zb5b?X`z(zbU=be(#v|A$~dS zrE0$=?VR?4t!@vp{#zc(@74=7E|!NHH|vEOU&}*{yY)l);j}j@-a`57v_Cpvf0^=! z<)Pxv<)Pxz@=$SWc}P2NxW(~iVkh;7^aFW_ACR|Xys5uZ{*FI8{Ox#I^ywSyZ&UtW z^dZG#DE}_HZOs0K%p2sDiibsiyW8<#$}fxlev|!U%0Dg-6>p3FN_nIudEM=XvVYN$ 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�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

    N2 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]BBS)`H{G8h9vc|vwECwJD}Um@^*2) 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

oiT@QdB!5RbHcRe5~?!<_9v5 zC>mE(d(Mw(O*_pqVXw|k&tj#wvD352@E0;&Q&c;nx6_Kt??mt~;uo0S0sfPt+B=IO zK2AH!eB|B7{2bLzv%UC;-2u*`V0s7G;rxPX@137{oL{iCk9qjDsCLa?lS-HE`OTYL z?_@(6f7>*)k&)8$`4S^h`3>21|N2Z!&3w>6ed~J(II~yE+ny&neHoPGiL%r<_xOa++=r!B5z-X zu8QfRP$vsc>h^?Ap-fXRp5ZsP=*;z5TDu;WAq2X<*E6CvH_y?WX1JzvT8bYdKCtlk5K!O)9D#vnjhj+Y`vI zJt4CMezO&ImZ&}*f%kuUV(RE|y#F$D?F`R44EMl4S+>>Q`lli9(5;Yn`*O&;b1dZD zvk>xrv=s6l+a2;wUkrKY{>Z&}CRTkb6>HyqBDH7iQG0KmuN*_%7sY5@`G)06`#=B9OT`^lLtnVzYU6>8mUXJK s&hHCib@8j+vW`qeJ3;MB>8z90lzA6MR>j+|-qTNreq0bQPF<IA>JDE3ugjsasN-Tc-f~1LYpq-Sw zNp&~iN^#>d5{nTO6gRU_5!{R$(VYviE*t40-DMeRBVD+dO(6k=ekeLg6)N}-dKUNG z_wL~w&KX%<_RrNz(wCMFcwyl@NmX(Zo?DdBa_L$m{aR41M1BxfYt=?{)vttCG{v*L z<8w3kpTKi<8HM$gXtBDqWb%d|)anv>*eC_EjBLut{9yHB8I=j)u_n7s`Oi}_>IV$# z%`h8sk#7(kJMdH8Qw$p8yg}#l2e`%nIt9`-Eh6r5 zlG1(lc;uVO$H8Zz0 zZR9>s$L>Rwh#}j7P34=F<-sLg4Anz8f+uWJCU-sOw1LCMB#qfUG_<~$`wL64d-R_l zu|26et?^llvHyd13S#BmCm0u}zT1W8MFSFuodld)s6p1|Hr@S2ot8p~bA#=kwYc45 nsErovJtcX~5wop1*hhQB9<5{YMWLP33_^Yb_KVA$Kmz~({b=-y literal 0 HcmV?d00001 From bc4f9efa5d2a07affc5d0055e87c5bf5d655cc72 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Jan 2019 23:21:37 +0100 Subject: [PATCH 179/335] One more test for probing with a slightly more complex hierarchy. --- src/db/unit_tests/dbLayoutToNetlistTests.cc | 242 ++++++++++++++++++ .../algo/device_extract_au2_with_rec_nets.gds | Bin 0 -> 40390 bytes testdata/algo/device_extract_l2.gds | Bin 0 -> 2634 bytes 3 files changed, 242 insertions(+) create mode 100644 testdata/algo/device_extract_au2_with_rec_nets.gds create mode 100644 testdata/algo/device_extract_l2.gds diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index b1ace3ea0..10ecc840c 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -432,3 +432,245 @@ TEST(1_Basic) EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2:$2"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2"); } + +TEST(2_Probing) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l2.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell)); + std::auto_ptr ractive (l2n.make_layer (active)); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + + // derived regions + + db::Region rpactive = *ractive & *rnwell; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = *ractive - *rnwell; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + + // NOTE: the device extractor will add more debug layers for the transistors: + // 20/0 -> Diffusion + // 21/0 -> Gate + MOSFETExtractor pmos_ex ("PMOS", &ly); + MOSFETExtractor nmos_ex ("NMOS", &ly); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC)\n" + " XINV2PAIR $2 ($1=$I18,$2=VDD,$3=VSS,$4=FB,$5=$I9)\n" + " XINV2PAIR $3 ($1=$I19,$2=VDD,$3=VSS,$4=$I9,$5=$I1)\n" + " XINV2PAIR $4 ($1=$I20,$2=VDD,$3=VSS,$4=$I1,$5=$I2)\n" + " XINV2PAIR $5 ($1=$I21,$2=VDD,$3=VSS,$4=$I2,$5=$I3)\n" + "Circuit INV2PAIR ($1=$I7,$2=$I5,$3=$I4,$4=$I2,$5=$I1):\n" + " XINV2 $1 (IN=$I3,$2=$I7,OUT=$I1,$4=$I4,$5=$I5)\n" + " XINV2 $2 (IN=$I2,$2=$I6,OUT=$I3,$4=$I4,$5=$I5)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" + " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" + " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" + " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // do some probing before purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I18"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I3"); + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n" + " XINV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC)\n" + " XINV2PAIR $2 ($1=(null),$2=VDD,$3=VSS,$4=FB,$5=$I9)\n" + " XINV2PAIR $3 ($1=(null),$2=VDD,$3=VSS,$4=$I9,$5=$I1)\n" + " XINV2PAIR $4 ($1=(null),$2=VDD,$3=VSS,$4=$I1,$5=$I2)\n" + " XINV2PAIR $5 ($1=(null),$2=VDD,$3=VSS,$4=$I2,$5=$I3)\n" + "Circuit INV2PAIR ($1=$I7,$2=$I5,$3=$I4,$4=$I2,$5=$I1):\n" + " XINV2 $1 (IN=$I3,$2=$I7,OUT=$I1,$4=$I4,$5=$I5)\n" + " XINV2 $2 (IN=$I2,$2=(null),OUT=$I3,$4=$I4,$5=$I5)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au2_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + // the transistor which supplies this probe target has been optimized away by "purge". + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I7"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I3"); +} diff --git a/testdata/algo/device_extract_au2_with_rec_nets.gds b/testdata/algo/device_extract_au2_with_rec_nets.gds new file mode 100644 index 0000000000000000000000000000000000000000..ddd8db94cf6c971b9b32e2f9cee6394876bdeae6 GIT binary patch literal 40390 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/device_extract_l2.gds b/testdata/algo/device_extract_l2.gds new file mode 100644 index 0000000000000000000000000000000000000000..9a9df7ea0453981cd8521178eaa4321ac4c0b154 GIT binary patch literal 2634 zcma)-Pe@cz6vofId2`?V(@dqXT$B{FFoQB91W~g;gQ4L}QX=TGO%NmzVG)#INf6Y^ zT11pB+y#N4tB43!ErTM2)`8&4A?Nj-ci!>6C(cu0{LOste)rsa&$;K3AKP~!*=ZtE zW06Is)*_?~2XUTY=KwNbg{P2}Z%`={Aei4l2f4kR8DVFv8vd6gNqjL~`g9qI{% zc9^)q_3G`4=o^Z095efLP}tZlDKit*&KsxGihYeTi82$&+e@YD%ozU)VztXD+pi^G zO4XC?5x?e3?%Nt_#31e(Qpfk^uTphpH9tl1e&aghO-gMnc)#KK` zK8hkNxh~V;+DDBjRX3%``qSb2Ql5sV%XQ1s*vn_4)Lo_O%s4;t)h7P@NAW9F&$LDS ziD-PT6YURGs?c{h4|3XWxrosd{EU-k<9f zfBv-im8$1D#h>5ocl#{76}_Is-3|FMP3T&YQRo>uYO@=po6O+V5)#C3uO0eeUi#pb zp285>Ib5oG>5J(Geq$!6nY)j9CvzV}m*$S+|K82~cn=R11}+X_C73-gvwP7vLP{d= zy^`KCKFQqt9j+(sj$S|6dLPSQ!8N>&jDcpx=0aZ6Mk@z7$(e+d87dlf||8e#URgd$J-4;KS zed9L+vfqF1oxfj?*nd!IW2+dSCa$#NIlt1j8;45w>-Iu#q;4a#@hJRZw&7xNhuQGX O Date: Sun, 6 Jan 2019 01:32:20 +0100 Subject: [PATCH 180/335] WIP: added ability to export nets to layouts. --- src/db/db/dbLayoutToNetlist.cc | 113 ++++++++++++++++++ src/db/db/dbLayoutToNetlist.h | 47 ++++++++ src/db/db/dbNetlist.h | 2 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 110 +++++++++++++++++ .../algo/device_extract_au1_rebuild_ff.gds | Bin 0 -> 3592 bytes .../algo/device_extract_au1_rebuild_fr.gds | Bin 0 -> 34686 bytes .../algo/device_extract_au1_rebuild_nf.gds | Bin 0 -> 4324 bytes .../algo/device_extract_au1_rebuild_nr.gds | Bin 0 -> 36354 bytes 8 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 testdata/algo/device_extract_au1_rebuild_ff.gds create mode 100644 testdata/algo/device_extract_au1_rebuild_fr.gds create mode 100644 testdata/algo/device_extract_au1_rebuild_nf.gds create mode 100644 testdata/algo/device_extract_au1_rebuild_nr.gds diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index d9c3052b4..af2026460 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -287,6 +287,119 @@ db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region return res.release (); } +void +LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, std::map, db::cell_index_type> &cmap) const +{ + for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { + shapes_of_net (net, *l->second, false, target_cell.shapes (l->first)); + } + + if (! cell_name_prefix) { + return; + } + + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + const db::connected_clusters &clusters = m_netex.clusters ().clusters_per_cell (circuit->cell_index ()); + typedef db::connected_clusters::connections_type connections_type; + const connections_type &connections = clusters.connections_for_cluster (net.cluster_id ()); + for (connections_type::const_iterator c = connections.begin (); c != connections.end (); ++c) { + + db::cell_index_type ci = c->inst ().inst_ptr.cell_index (); + const db::Circuit *subcircuit = mp_netlist->circuit_by_cell_index (ci); + tl_assert (subcircuit != 0); + + const db::Net *subnet = subcircuit->net_by_cluster_id (c->id ()); + tl_assert (subnet != 0); + + std::map, db::cell_index_type>::const_iterator cm = cmap.find (std::make_pair (ci, c->id ())); + if (cm == cmap.end ()) { + + db::cell_index_type target_ci = target.add_cell ((std::string (cell_name_prefix) + subcircuit->name ()).c_str ()); + cm = cmap.insert (std::make_pair (std::make_pair (ci, c->id ()), target_ci)).first; + + build_net_rec (*subnet, target, target.cell (target_ci), lmap, cell_name_prefix, cmap); + + } + + target_cell.insert (db::CellInstArray (db::CellInst (cm->second), c->inst ().complex_trans ())); + + } +} + +void +LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix) const +{ + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + + std::map, db::cell_index_type> cell_map; + cell_map.insert (std::make_pair (std::make_pair (net.circuit ()->cell_index (), net.cluster_id ()), target_cell.cell_index ())); + + build_net_rec (net, target, target_cell, lmap, cell_name_prefix, cell_map); +} + +void +LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix) const +{ + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + + const db::Netlist *netlist = mp_netlist.get (); + for (db::Netlist::const_circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { + + if (! cmap.has_mapping (c->cell_index ())) { + continue; + } + + std::set excluded_nets; + if (circuit_cell_name_prefix) { + for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) { + excluded_nets.insert (c->net_for_pin (p->id ())); + } + } + + db::cell_index_type target_ci = cmap.cell_mapping (c->cell_index ()); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + if (excluded_nets.find (n.operator-> ()) == excluded_nets.end ()) { + + const db::connected_clusters &ccl = m_netex.clusters ().clusters_per_cell (c->cell_index ()); + const db::local_cluster &cl = ccl.cluster_by_id (n->cluster_id ()); + + bool any_connections = ! ccl.connections_for_cluster (n->cluster_id ()).empty (); + + bool any_shapes = false; + for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any_shapes; ++m) { + any_shapes = ! cl.begin (layer_of (*m->second)).at_end (); + } + + if (any_shapes || (circuit_cell_name_prefix && any_connections)) { + + db::cell_index_type net_ci = target_ci; + + if (net_cell_name_prefix) { + + db::Cell &tc = target.cell (target_ci); + net_ci = target.add_cell ((std::string (net_cell_name_prefix) + n->expanded_name ()).c_str ()); + tc.insert (db::CellInstArray (db::CellInst (net_ci), db::Trans ())); + + } + + build_net (*n, target, target.cell (net_ci), lmap, circuit_cell_name_prefix); + + } + + } + } + + } +} + db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point) { return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point); diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 4efaf4294..7e7f40689 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -235,6 +235,52 @@ public: */ void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const; + /** + * @brief Builds a net representation in the given layout and cell + * + * This method has two modes: recursive and top-level mode. In recursive mode, + * it will create a proper hierarchy below the given target cell to hold all subcircuits the + * net connects to. It will copy the net's parts from this subcircuits into these cells. + * + * In top-level mode, only the shapes from the net inside it's circuit are copied to + * the given target cell. No other cells are created. + * + * Recursive mode is picked when a cell name prefix is given. The new cells will be + * named like cell_name_prefix + circuit name. + * + * @param target The target layout + * @param target_cell The target cell + * @param lmap Target layer indexes (keys) and net regions (values) + * @param cell_name_prefix Chooses recursive mode if non-null + */ + void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix) const; + + /** + * @brief Builds a full hierarchical representation of the nets + * + * This method copies all nets into cells corresponding to the circuits. It uses the cmap + * object to determine the target cell (create them with "cell_mapping_into" or "const_cell_mapping_into"). + * If no mapping is requested, the specific circuit it skipped. + * + * The method has two net annotation modes: + * * No annotation (net_cell_name_prefix == 0): the shapes will be put into the target cell simply + * * Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created + * and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name). + * + * In addition, net hierarchy is covered in two ways: + * * No connection indicated (circuit_cell_name_prefix == 0: the net shapes are simply put into their + * respective circuits. The connections are not indicated. + * * Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built + * to accomodate the subnets (see build_net in recursive mode). + * + * @param cmap The mapping of internal layout to target layout for the circuit mapping + * @param target The target layout + * @param lmap Target layer indexes (keys) and net regions (values) + * @param circuit_cell_name_prefix See method description + * @param net_cell_name_prefix See method description + */ + void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix) const; + /** * @brief Finds the net by probing a specific location on the given layer * @@ -271,6 +317,7 @@ private: bool m_netlist_extracted; size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path); + void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, std::map, db::cell_index_type> &cmap) const; }; } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 73ea82d42..4c689389d 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -1534,7 +1534,7 @@ public: /** * @brief Returns true, if the net is an external net * - * External nets are net which are connected to an outgoing pin. + * External nets are nets which are connected to an outgoing pin. */ bool is_external_net (const db::Net *net) const; diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 10ecc840c..f63bdbf78 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -382,6 +382,116 @@ TEST(1_Basic) EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I39"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2"); + // test build_all_nets + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, 0, 0); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_rebuild_ff.gds"); + + db::compare_layouts (_this, ly2, au); + } + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", 0); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_rebuild_nf.gds"); + + db::compare_layouts (_this, ly2, au); + } + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, 0, "CIRCUIT_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_rebuild_fr.gds"); + + db::compare_layouts (_this, ly2, au); + } + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_rebuild_nr.gds"); + + db::compare_layouts (_this, ly2, au); + } + // doesn't do anything here, but we test that this does not destroy anything: l2n.netlist ()->combine_devices (); diff --git a/testdata/algo/device_extract_au1_rebuild_ff.gds b/testdata/algo/device_extract_au1_rebuild_ff.gds new file mode 100644 index 0000000000000000000000000000000000000000..afbbb50923d70b2e717885039342897f2cef4e28 GIT binary patch literal 3592 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 literal 0 HcmV?d00001 From 8d51d1e4bbf463d18067f8a66977f78d7f2e5fe3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Jan 2019 01:54:36 +0100 Subject: [PATCH 181/335] WIP: better optimization of hierarchical net output. --- src/db/db/dbLayoutToNetlist.cc | 5 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 48 ++++++++++++++++++ .../algo/device_extract_au1_rebuild_nr.gds | Bin 36354 -> 9874 bytes 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index af2026460..0e2adb5d0 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -336,7 +336,6 @@ LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &ta } std::map, db::cell_index_type> cell_map; - cell_map.insert (std::make_pair (std::make_pair (net.circuit ()->cell_index (), net.cluster_id ()), target_cell.cell_index ())); build_net_rec (net, target, target_cell, lmap, cell_name_prefix, cell_map); } @@ -348,6 +347,8 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); } + std::map, db::cell_index_type> cell_map; + const db::Netlist *netlist = mp_netlist.get (); for (db::Netlist::const_circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { @@ -390,7 +391,7 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target } - build_net (*n, target, target.cell (net_ci), lmap, circuit_cell_name_prefix); + build_net_rec (*n, target, target.cell (net_ci), lmap, circuit_cell_name_prefix, cell_map); } diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 1e47fddc8..2b6b003fc 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -151,6 +151,54 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "If 'recursive'' is true, the returned region will contain the shapes of\n" "all subcircuits too.\n" ) + + gsi::method ("build_net", &db::LayoutToNetlist::build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("cell_name"), + "@brief Builds a net representation in the given layout and cell\n" + "\n" + "This method has two modes: recursive and top-level mode. In recursive mode,\n" + "it will create a proper hierarchy below the given target cell to hold all subcircuits the\n" + "net connects to. It will copy the net's parts from this subcircuits into these cells.\n" + "\n" + "In top-level mode, only the shapes from the net inside it's circuit are copied to\n" + "the given target cell. No other cells are created.\n" + "\n" + "Recursive mode is picked when a cell name prefix is given. The new cells will be\n" + "named like cell_name_prefix + circuit name.\n" + "\n" + "@param target The target layout\n" + "@param target_cell The target cell\n" + "@param lmap Target layer indexes (keys) and net regions (values)\n" + "@param cell_name_prefix Chooses recursive mode if non-nil\n" + ) + + gsi::method ("build_all_nets", &db::LayoutToNetlist::build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix"), gsi::arg ("circuit_cell_name_prefix"), + "@brief Builds a full hierarchical representation of the nets\n" + "\n" + "This method copies all nets into cells corresponding to the circuits. It uses the cmap\n" + "object to determine the target cell (create them with \\cell_mapping_into or \\const_cell_mapping_into.\n" + "If no mapping is requested, the specific circuit it skipped.\n" + "\n" + "The method has two net annotation modes:\n" + "\n" + "@ul\n" + "@li 'No annotation'' (net_cell_name_prefix == 0): the shapes will be put into the target cell simply @/li\n" + "@li Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created\n" + " and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name). @/li\n" + "@/ul\n" + "\n" + "In addition, net hierarchy is covered in two ways:\n" + "\n" + "@ul\n" + "@li No connection indicated (circuit_cell_name_prefix == 0: the net shapes are simply put into their\n" + " respective circuits. The connections are not indicated. @/li\n" + "@li Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built\n" + " to accomodate the subnets (see build_net in recursive mode). @/li\n" + "@/ul\n" + "\n" + "@param cmap The mapping of internal layout to target layout for the circuit mapping\n" + "@param target The target layout\n" + "@param lmap Target layer indexes (keys) and net regions (values)\n" + "@param circuit_cell_name_prefix See method description\n" + "@param net_cell_name_prefix See method description\n" + ) + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), "@brief Finds the net by probing a specific location on the given layer\n" "\n" diff --git a/testdata/algo/device_extract_au1_rebuild_nr.gds b/testdata/algo/device_extract_au1_rebuild_nr.gds index ef77178c545b852d9518460564c779aa602ad854..19301649d9a82d85a444a52f72759d322613b88e 100644 GIT binary patch delta 1216 zcmZpg!!*fPiGhuQi7A3XhLMT=F#{uz$H-vHz>LhEsHDk{CN#N9_9=!~o*brFl6*3T zx@VahlP~14O`ewKFuAUnZL(XE%H$8e9Gly+`WZ14tK`5Gt4w}Sz&+VFNN93>G3(@O zdB&4tgLyaK$f?HAv^gnHo(bKg%@Ybb`pS&1a&ljpv=o|$8F<*({9HrgRXhzR z|8EqV{IN}SQaS(R+D3uNiXEzxW9sneFoNj-D&v`4)hG{igV~ZJFi(CW3KLOPQY%+&y3#dle^bem+ z7H}UGPam}s@(#pg^)T+kUE0KnHYeL z1EvVvR+<=s)j*Q~9(6`wb-*~sqsADl1{%?L)R}@A8dIem$pxKrF%-&lJ!1v{pa~kZ literal 36354 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 From 261b14a260eca197741f2d3f2ac65aeb91db3109 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Jan 2019 02:15:04 +0100 Subject: [PATCH 182/335] WIP: GSI binding of LayoutToNetlist::build_nets --- src/db/db/gsiDeclDbLayoutToNetlist.cc | 17 +++++++++++++++-- .../algo/device_extract_au1_rebuild_fr.gds | Bin 34686 -> 8206 bytes 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 2b6b003fc..a9e50a9be 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -43,6 +43,19 @@ static db::Cell *l2n_internal_top_cell (db::LayoutToNetlist *l2n) return const_cast (l2n->internal_top_cell ()); } +static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &cell_name_prefix) +{ + std::string p = cell_name_prefix.to_string (); + l2n->build_net (net, target, target_cell, lmap, cell_name_prefix.is_nil () ? 0 : p.c_str ()); +} + +static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &circuit_cell_name_prefix) +{ + std::string cp = circuit_cell_name_prefix.to_string (); + std::string np = net_cell_name_prefix.to_string (); + l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str ()); +} + Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::constructor ("new", &make_l2n, gsi::arg ("iter"), "@brief The constructor\n" @@ -151,7 +164,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "If 'recursive'' is true, the returned region will contain the shapes of\n" "all subcircuits too.\n" ) + - gsi::method ("build_net", &db::LayoutToNetlist::build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("cell_name"), + gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("cell_name"), "@brief Builds a net representation in the given layout and cell\n" "\n" "This method has two modes: recursive and top-level mode. In recursive mode,\n" @@ -169,7 +182,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "@param lmap Target layer indexes (keys) and net regions (values)\n" "@param cell_name_prefix Chooses recursive mode if non-nil\n" ) + - gsi::method ("build_all_nets", &db::LayoutToNetlist::build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix"), gsi::arg ("circuit_cell_name_prefix"), + gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix"), gsi::arg ("circuit_cell_name_prefix"), "@brief Builds a full hierarchical representation of the nets\n" "\n" "This method copies all nets into cells corresponding to the circuits. It uses the cmap\n" diff --git a/testdata/algo/device_extract_au1_rebuild_fr.gds b/testdata/algo/device_extract_au1_rebuild_fr.gds index 093c327a65ea4a78c6e6937879744359a79cfd88..4673a339ef10cda6cd2e53747b38ed06b29ac6db 100644 GIT binary patch delta 573 zcmey@$JFPb#K6YD#1z3G!^p(`n1K<*W8h`rMrKb`QWwM!oLnXQ7>j7095&G;`9y0B z6BxMI*g}FF{el@7SlAgDWLR-JV6s4#2BXnr#&pfe7vf|$*Jtfx#A3By4oK0%0+Y$H z3W7|A43i6#%qLg-^KNF!t;C{!b5EWu6Q(hncNM(Erg}@!XJ$;*lXsNO#uVM0Qf|*a zxwFD}azGtFm^NsJ&STi&`N{H? zsz4JV{5=&AKFH+L6{cWuki0@QR6ST+0IcUu6+{dq2U6ou3*m#rL41(yATvPXAie-p WJwzO02FQMpI|QnDHvedCV*~(rn~~@M literal 34686 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 Date: Sun, 6 Jan 2019 12:53:22 +0100 Subject: [PATCH 183/335] WIP: refactoring - separated pins of net into outgoing and subcircuit. --- src/db/db/dbLayoutToNetlist.cc | 54 +++---- src/db/db/dbNetlist.cc | 152 ++++++++++------- src/db/db/dbNetlist.h | 171 +++++++++++++++++--- src/db/db/gsiDeclDbNetlist.cc | 50 ++++-- src/db/unit_tests/dbLayoutToNetlistTests.cc | 2 +- src/db/unit_tests/dbNetlistTests.cc | 50 +++--- testdata/ruby/dbNetlist.rb | 25 ++- 7 files changed, 342 insertions(+), 162 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 0e2adb5d0..5a3bcfe8a 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -356,46 +356,41 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target continue; } - std::set excluded_nets; - if (circuit_cell_name_prefix) { - for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) { - excluded_nets.insert (c->net_for_pin (p->id ())); - } - } - db::cell_index_type target_ci = cmap.cell_mapping (c->cell_index ()); for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - if (excluded_nets.find (n.operator-> ()) == excluded_nets.end ()) { + // exlude local nets in recursive mode + if (circuit_cell_name_prefix && n->pin_count () > 0) { + continue; + } - const db::connected_clusters &ccl = m_netex.clusters ().clusters_per_cell (c->cell_index ()); - const db::local_cluster &cl = ccl.cluster_by_id (n->cluster_id ()); + const db::connected_clusters &ccl = m_netex.clusters ().clusters_per_cell (c->cell_index ()); + const db::local_cluster &cl = ccl.cluster_by_id (n->cluster_id ()); - bool any_connections = ! ccl.connections_for_cluster (n->cluster_id ()).empty (); + bool any_connections = ! ccl.connections_for_cluster (n->cluster_id ()).empty (); - bool any_shapes = false; - for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any_shapes; ++m) { - any_shapes = ! cl.begin (layer_of (*m->second)).at_end (); - } + bool any_shapes = false; + for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any_shapes; ++m) { + any_shapes = ! cl.begin (layer_of (*m->second)).at_end (); + } - if (any_shapes || (circuit_cell_name_prefix && any_connections)) { + if (any_shapes || (circuit_cell_name_prefix && any_connections)) { - db::cell_index_type net_ci = target_ci; + db::cell_index_type net_ci = target_ci; - if (net_cell_name_prefix) { + if (net_cell_name_prefix) { - db::Cell &tc = target.cell (target_ci); - net_ci = target.add_cell ((std::string (net_cell_name_prefix) + n->expanded_name ()).c_str ()); - tc.insert (db::CellInstArray (db::CellInst (net_ci), db::Trans ())); - - } - - build_net_rec (*n, target, target.cell (net_ci), lmap, circuit_cell_name_prefix, cell_map); + db::Cell &tc = target.cell (target_ci); + net_ci = target.add_cell ((std::string (net_cell_name_prefix) + n->expanded_name ()).c_str ()); + tc.insert (db::CellInstArray (db::CellInst (net_ci), db::Trans ())); } + build_net_rec (*n, target, target.cell (net_ci), lmap, circuit_cell_name_prefix, cell_map); + } + } } @@ -484,16 +479,11 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin // follow the path up in the net hierarchy using the transformation and the upper cell index as the // guide line - while (! inst_path.empty () && circuit->is_external_net (net)) { + while (! inst_path.empty () && net->pin_count () > 0) { cell_indexes.pop_back (); - db::Pin *pin = 0; - for (db::Circuit::pin_iterator p = circuit->begin_pins (); p != circuit->end_pins () && ! pin; ++p) { - if (circuit->net_for_pin (p->id ()) == net) { - pin = p.operator-> (); - } - } + const db::Pin *pin = circuit->pin_by_id (net->begin_pins ()->pin_id ()); tl_assert (pin != 0); db::DCplxTrans dtrans = dbu_trans * inst_path.back ().complex_trans () * dbu_trans_inv; diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 505604177..914f6a882 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -191,9 +191,9 @@ SubCircuit::SubCircuit () SubCircuit::~SubCircuit() { - for (std::vector::const_iterator p = m_pin_refs.begin (); p != m_pin_refs.end (); ++p) { - if (*p != Net::pin_iterator () && (*p)->net ()) { - (*p)->net ()->erase_pin (*p); + for (std::vector::const_iterator p = m_pin_refs.begin (); p != m_pin_refs.end (); ++p) { + if (*p != Net::subcircuit_pin_iterator () && (*p)->net ()) { + (*p)->net ()->erase_subcircuit_pin (*p); } } } @@ -233,10 +233,10 @@ void SubCircuit::set_trans (const db::DCplxTrans &t) m_trans = t; } -void SubCircuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter) +void SubCircuit::set_pin_ref_for_pin (size_t pin_id, Net::subcircuit_pin_iterator iter) { if (m_pin_refs.size () < pin_id + 1) { - m_pin_refs.resize (pin_id + 1, Net::pin_iterator ()); + m_pin_refs.resize (pin_id + 1, Net::subcircuit_pin_iterator ()); } m_pin_refs [pin_id] = iter; } @@ -255,8 +255,8 @@ void SubCircuit::set_circuit_ref (Circuit *c) const Net *SubCircuit::net_for_pin (size_t pin_id) const { if (pin_id < m_pin_refs.size ()) { - Net::pin_iterator p = m_pin_refs [pin_id]; - if (p != Net::pin_iterator ()) { + Net::subcircuit_pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::subcircuit_pin_iterator ()) { return p->net (); } } @@ -270,15 +270,15 @@ void SubCircuit::connect_pin (size_t pin_id, Net *net) } if (pin_id < m_pin_refs.size ()) { - Net::pin_iterator p = m_pin_refs [pin_id]; - if (p != Net::pin_iterator () && p->net ()) { - p->net ()->erase_pin (p); + Net::subcircuit_pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::subcircuit_pin_iterator () && p->net ()) { + p->net ()->erase_subcircuit_pin (p); } - m_pin_refs [pin_id] = Net::pin_iterator (); + m_pin_refs [pin_id] = Net::subcircuit_pin_iterator (); } if (net) { - net->add_pin (NetPinRef (this, pin_id)); + net->add_subcircuit_pin (NetSubcircuitPinRef (this, pin_id)); } } @@ -333,25 +333,19 @@ NetTerminalRef::device_class () const // NetPinRef class implementation NetPinRef::NetPinRef () - : m_pin_id (0), mp_subcircuit (0), mp_net (0) + : m_pin_id (0), mp_net (0) { // .. nothing yet .. } NetPinRef::NetPinRef (size_t pin_id) - : m_pin_id (pin_id), mp_subcircuit (0), mp_net (0) -{ - // .. nothing yet .. -} - -NetPinRef::NetPinRef (SubCircuit *circuit, size_t pin_id) - : m_pin_id (pin_id), mp_subcircuit (circuit), mp_net (0) + : m_pin_id (pin_id), mp_net (0) { // .. nothing yet .. } NetPinRef::NetPinRef (const NetPinRef &other) - : m_pin_id (other.m_pin_id), mp_subcircuit (other.mp_subcircuit), mp_net (0) + : m_pin_id (other.m_pin_id), mp_net (0) { // .. nothing yet .. } @@ -360,18 +354,51 @@ NetPinRef &NetPinRef::operator= (const NetPinRef &other) { if (this != &other) { m_pin_id = other.m_pin_id; - mp_subcircuit = other.mp_subcircuit; } return *this; } const Pin *NetPinRef::pin () const { - if (! mp_subcircuit) { - if (mp_net && mp_net->circuit ()) { - return mp_net->circuit ()->pin_by_id (m_pin_id); - } - } else if (mp_subcircuit->circuit_ref ()) { + if (mp_net && mp_net->circuit ()) { + return mp_net->circuit ()->pin_by_id (m_pin_id); + } + return 0; +} + +// -------------------------------------------------------------------------------- +// NetSubcircuitPinRef class implementation + +NetSubcircuitPinRef::NetSubcircuitPinRef () + : m_pin_id (0), mp_subcircuit (0), mp_net (0) +{ + // .. nothing yet .. +} + +NetSubcircuitPinRef::NetSubcircuitPinRef (SubCircuit *circuit, size_t pin_id) + : m_pin_id (pin_id), mp_subcircuit (circuit), mp_net (0) +{ + // .. nothing yet .. +} + +NetSubcircuitPinRef::NetSubcircuitPinRef (const NetSubcircuitPinRef &other) + : m_pin_id (other.m_pin_id), mp_subcircuit (other.mp_subcircuit), mp_net (0) +{ + // .. nothing yet .. +} + +NetSubcircuitPinRef &NetSubcircuitPinRef::operator= (const NetSubcircuitPinRef &other) +{ + if (this != &other) { + m_pin_id = other.m_pin_id; + mp_subcircuit = other.mp_subcircuit; + } + return *this; +} + +const Pin *NetSubcircuitPinRef::pin () const +{ + if (mp_subcircuit && mp_subcircuit->circuit_ref ()) { return mp_subcircuit->circuit_ref ()->pin_by_id (m_pin_id); } return 0; @@ -407,6 +434,10 @@ Net &Net::operator= (const Net &other) m_name = other.m_name; m_cluster_id = other.m_cluster_id; + for (const_subcircuit_pin_iterator i = other.begin_subcircuit_pins (); i != other.end_subcircuit_pins (); ++i) { + add_subcircuit_pin (*i); + } + for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { add_pin (*i); } @@ -436,6 +467,10 @@ void Net::clear () while (! m_pins.empty ()) { erase_pin (begin_pins ()); } + + while (! m_subcircuit_pins.empty ()) { + erase_subcircuit_pin (begin_subcircuit_pins ()); + } } void Net::set_name (const std::string &name) @@ -483,25 +518,37 @@ void Net::add_pin (const NetPinRef &pin) NetPinRef &new_pin = m_pins.back (); new_pin.set_net (this); - if (! pin.subcircuit ()) { - if (mp_circuit) { - mp_circuit->set_pin_ref_for_pin (new_pin.pin_id (), --m_pins.end ()); - } - } else { - new_pin.subcircuit ()->set_pin_ref_for_pin (new_pin.pin_id (), --m_pins.end ()); + if (mp_circuit) { + mp_circuit->set_pin_ref_for_pin (new_pin.pin_id (), --m_pins.end ()); } } +void Net::add_subcircuit_pin (const NetSubcircuitPinRef &pin) +{ + m_subcircuit_pins.push_back (pin); + NetSubcircuitPinRef &new_pin = m_subcircuit_pins.back (); + new_pin.set_net (this); + + tl_assert (pin.subcircuit () != 0); + new_pin.subcircuit ()->set_pin_ref_for_pin (new_pin.pin_id (), --m_subcircuit_pins.end ()); +} + void Net::erase_pin (pin_iterator iter) { - if (iter->subcircuit ()) { - iter->subcircuit ()->set_pin_ref_for_pin (iter->pin_id (), pin_iterator ()); - } else if (mp_circuit) { + if (mp_circuit) { mp_circuit->set_pin_ref_for_pin (iter->pin_id (), pin_iterator ()); } m_pins.erase (iter); } +void Net::erase_subcircuit_pin (subcircuit_pin_iterator iter) +{ + if (iter->subcircuit ()) { + iter->subcircuit ()->set_pin_ref_for_pin (iter->pin_id (), subcircuit_pin_iterator ()); + } + m_subcircuit_pins.erase (iter); +} + void Net::add_terminal (const NetTerminalRef &terminal) { if (! terminal.device ()) { @@ -612,13 +659,13 @@ Circuit &Circuit::operator= (const Circuit &other) } for (Net::const_pin_iterator p = i->begin_pins (); p != i->end_pins (); ++p) { - if (! p->subcircuit ()) { - n->add_pin (NetPinRef (p->pin_id ())); - } else { - std::map::const_iterator m = sc_table.find (p->subcircuit ()); - tl_assert (m != sc_table.end ()); - n->add_pin (NetPinRef (m->second, p->pin_id ())); - } + n->add_pin (NetPinRef (p->pin_id ())); + } + + for (Net::const_subcircuit_pin_iterator p = i->begin_subcircuit_pins (); p != i->end_subcircuit_pins (); ++p) { + std::map::const_iterator m = sc_table.find (p->subcircuit ()); + tl_assert (m != sc_table.end ()); + n->add_subcircuit_pin (NetSubcircuitPinRef (m->second, p->pin_id ())); } } @@ -869,21 +916,6 @@ void Circuit::connect_pin (size_t pin_id, Net *net) } } -bool Circuit::is_external_net (const db::Net *net) const -{ - if (!net || net->pin_count () == 0) { - return false; - } - - for (std::vector::const_iterator p = m_pin_refs.begin (); p != m_pin_refs.end (); ++p) { - if (*p != Net::pin_iterator () && (*p)->net () == net) { - return true; - } - } - - return false; -} - void Circuit::purge_nets () { std::vector nets_to_be_purged; @@ -1541,7 +1573,7 @@ void Netlist::make_top_level_pins () // create pins for the named nets and connect them for (Circuit::net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { - if (! n->name ().empty () && n->terminal_count () + n->pin_count () > 0) { + if (! n->name ().empty () && n->terminal_count () + n->subcircuit_pin_count () > 0) { Pin pin = circuit->add_pin (n->name ()); circuit->connect_pin (pin.id (), n.operator-> ()); } @@ -1633,7 +1665,7 @@ std::string Netlist::to_string () const #if 0 // for debugging for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n"; + res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " sc_pins=" + tl::to_string (n->subcircuit_pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n"; } #endif diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 4c689389d..7d048db5a 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -260,9 +260,7 @@ private: /** * @brief A reference to a pin inside a net * - * A pin belongs to a subcircuit. - * If the subcircuit reference is 0, the pin is a pin of the current circuit - * (upward pin). + * This object describes a connection to an outgoing pin. */ class DB_PUBLIC NetPinRef { @@ -277,11 +275,6 @@ public: */ NetPinRef (size_t pin_id); - /** - * @brief Creates a pin reference to the given pin of the given subcircuit - */ - NetPinRef (SubCircuit *circuit, size_t pin_id); - /** * @brief Copy constructor */ @@ -296,6 +289,95 @@ public: * @brief Comparison */ bool operator< (const NetPinRef &other) const + { + return m_pin_id < other.m_pin_id; + } + + /** + * @brief Equality + */ + bool operator== (const NetPinRef &other) const + { + return (m_pin_id == other.m_pin_id); + } + + /** + * @brief Gets the pin reference (const version) + */ + size_t pin_id () const + { + return m_pin_id; + } + + /** + * @brief Gets the pin reference from the pin id + * If the pin cannot be resolved, null is returned. + */ + const Pin *pin () const; + + /** + * @brief Gets the net the pin lives in + */ + Net *net () + { + return mp_net; + } + + /** + * @brief Gets the net the pin lives in (const version) + */ + const Net *net () const + { + return mp_net; + } + +private: + friend class Net; + + size_t m_pin_id; + Net *mp_net; + + /** + * @brief Sets the net the terminal lives in + */ + void set_net (Net *net) + { + mp_net = net; + } +}; + +/** + * @brief A reference to a pin inside a net + * + * This object describes a connection to a pin of a subcircuit. + */ +class DB_PUBLIC NetSubcircuitPinRef +{ +public: + /** + * @brief Default constructor + */ + NetSubcircuitPinRef (); + + /** + * @brief Creates a pin reference to the given pin of the given subcircuit + */ + NetSubcircuitPinRef (SubCircuit *circuit, size_t pin_id); + + /** + * @brief Copy constructor + */ + NetSubcircuitPinRef (const NetSubcircuitPinRef &other); + + /** + * @brief Assignment + */ + NetSubcircuitPinRef &operator= (const NetSubcircuitPinRef &other); + + /** + * @brief Comparison + */ + bool operator< (const NetSubcircuitPinRef &other) const { if (mp_subcircuit != other.mp_subcircuit) { return mp_subcircuit < other.mp_subcircuit; @@ -306,7 +388,7 @@ public: /** * @brief Equality */ - bool operator== (const NetPinRef &other) const + bool operator== (const NetSubcircuitPinRef &other) const { return (mp_subcircuit == other.mp_subcircuit && m_pin_id == other.m_pin_id); } @@ -388,6 +470,9 @@ public: typedef std::list pin_list; typedef pin_list::const_iterator const_pin_iterator; typedef pin_list::iterator pin_iterator; + typedef std::list subcircuit_pin_list; + typedef subcircuit_pin_list::const_iterator const_subcircuit_pin_iterator; + typedef subcircuit_pin_list::iterator subcircuit_pin_iterator; /** * @brief Constructor @@ -525,6 +610,48 @@ public: return m_pins.end (); } + /** + * @brief Adds a subcircuit pin to this net + */ + void add_subcircuit_pin (const NetSubcircuitPinRef &pin); + + /** + * @brief Erases the given subcircuit pin from this net + */ + void erase_subcircuit_pin (subcircuit_pin_iterator iter); + + /** + * @brief Begin iterator for the pins of the net (const version) + */ + const_subcircuit_pin_iterator begin_subcircuit_pins () const + { + return m_subcircuit_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (const version) + */ + const_subcircuit_pin_iterator end_subcircuit_pins () const + { + return m_subcircuit_pins.end (); + } + + /** + * @brief Begin iterator for the pins of the net (non-const version) + */ + subcircuit_pin_iterator begin_subcircuit_pins () + { + return m_subcircuit_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (non-const version) + */ + subcircuit_pin_iterator end_subcircuit_pins () + { + return m_subcircuit_pins.end (); + } + /** * @brief Adds a terminal to this net */ @@ -572,7 +699,7 @@ public: */ bool is_floating () const { - return (m_pins.size () + m_terminals.size ()) < 2; + return (m_pins.size () + m_subcircuit_pins.size () + m_terminals.size ()) < 2; } /** @@ -580,17 +707,25 @@ public: */ bool is_internal () const { - return m_pins.size () == 0 && m_terminals.size () == 2; + return m_pins.size () == 0 && m_subcircuit_pins.size () == 0 && m_terminals.size () == 2; } /** - * @brief Returns the number of pins connected + * @brief Returns the number of outgoing pins connected */ size_t pin_count () const { return m_pins.size (); } + /** + * @brief Returns the number of subcircuit pins connected + */ + size_t subcircuit_pin_count () const + { + return m_subcircuit_pins.size (); + } + /** * @brief Returns the number of terminals connected */ @@ -604,6 +739,7 @@ private: terminal_list m_terminals; pin_list m_pins; + subcircuit_pin_list m_subcircuit_pins; std::string m_name; size_t m_cluster_id; Circuit *mp_circuit; @@ -971,14 +1107,14 @@ private: tl::weak_ptr m_circuit_ref; std::string m_name; db::DCplxTrans m_trans; - std::vector m_pin_refs; + std::vector m_pin_refs; size_t m_id; Circuit *mp_circuit; /** * @brief Sets the pin reference for a specific pin */ - void set_pin_ref_for_pin (size_t ppin_id, Net::pin_iterator iter); + void set_pin_ref_for_pin (size_t ppin_id, Net::subcircuit_pin_iterator iter); /** * @brief Sets the circuit reference @@ -1531,13 +1667,6 @@ public: */ void connect_pin (size_t pin_id, Net *net); - /** - * @brief Returns true, if the net is an external net - * - * External nets are nets which are connected to an outgoing pin. - */ - bool is_external_net (const db::Net *net) const; - /** * @brief Purge unused nets * diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 10957428a..86b357b54 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -236,18 +236,31 @@ Class decl_dbNetPinRef ("db", "NetPinRef", gsi::method ("pin", &db::NetPinRef::pin, "@brief Gets the \\Pin object of the pin the connection is made to." ) + - gsi::method ("subcircuit", (db::SubCircuit *(db::NetPinRef::*) ()) &db::NetPinRef::subcircuit, - "@brief Gets the subcircuit reference.\n" - "If the pin is a pin of a subcircuit, this attribute " - "indicates the subcircuit the net attaches to. The " - "subcircuit lives in the same circuit than the net. " - "If the pin is a outgoing pin of the circuit, this " - "attribute is nil." - ) + gsi::method ("net", (db::Net *(db::NetPinRef::*) ()) &db::NetPinRef::net, "@brief Gets the net this pin reference is attached to" ), - "@brief A connection to a pin of a subcircuit or an outgoing pin of the circuit.\n" + "@brief A connection to an outgoing pin of the circuit.\n" + "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_dbNetSubcircuitPinRef ("db", "NetSubcircuitPinRef", + gsi::method ("pin_id", &db::NetSubcircuitPinRef::pin_id, + "@brief Gets the ID of the pin the connection is made to." + ) + + gsi::method ("pin", &db::NetSubcircuitPinRef::pin, + "@brief Gets the \\Pin object of the pin the connection is made to." + ) + + gsi::method ("subcircuit", (db::SubCircuit *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::subcircuit, + "@brief Gets the subcircuit reference.\n" + "This attribute indicates the subcircuit the net attaches to. The " + "subcircuit lives in the same circuit than the net. " + ) + + gsi::method ("net", (db::Net *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::net, + "@brief Gets the net this pin reference is attached to" + ), + "@brief A connection to a pin of a subcircuit.\n" "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" "\n" "This class has been added in version 0.26." @@ -288,10 +301,14 @@ Class decl_dbNet ("db", "Net", "See \\cluster_id= for details about the cluster ID." ) + gsi::iterator ("each_pin", (db::Net::pin_iterator (db::Net::*) ()) &db::Net::begin_pins, (db::Net::pin_iterator (db::Net::*) ()) &db::Net::end_pins, - "@brief Iterates over all pins the net connects.\n" + "@brief Iterates over all outgoing pins the net connects.\n" "Pin connections are described by \\NetPinRef objects. Pin connections " - "are either connections to subcircuit pins or to outgoing pins of the " - "circuit the net lives in." + "are connections to outgoing pins of the circuit the net lives in." + ) + + gsi::iterator ("each_subcircuit_pin", (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::begin_subcircuit_pins, (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::end_subcircuit_pins, + "@brief Iterates over all subcircuit pins the net connects.\n" + "Subcircuit pin connections are described by \\NetSubcircuitPinRef objects. These are " + "connections to specific pins of subcircuits." ) + gsi::iterator ("each_terminal", (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::begin_terminals, (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::end_terminals, "@brief Iterates over all terminals the net connects.\n" @@ -307,7 +324,10 @@ Class decl_dbNet ("db", "Net", "Internal nets are those which connect exactly two terminals and nothing else (pin_count = 0 and terminal_count == 2)." ) + gsi::method ("pin_count", &db::Net::pin_count, - "@brief Returns the number of pins connected by this net.\n" + "@brief Returns the number of outgoing pins connected by this net.\n" + ) + + gsi::method ("subcircuit_pin_count", &db::Net::subcircuit_pin_count, + "@brief Returns the number of subcircuit pins connected by this net.\n" ) + gsi::method ("terminal_count", &db::Net::terminal_count, "@brief Returns the number of terminals connected by this net.\n" @@ -765,10 +785,6 @@ Class decl_dbCircuit ("db", "Circuit", "@brief Gets the cell index of the circuit\n" "See \\cell_index= for details.\n" ) + - gsi::method ("is_external_net?", &db::Circuit::is_external_net, gsi::arg ("net"), - "@brief Returns true, if the given net is an external one.\n" - "External nets are nets which are connected to an outgoing pin." - ) + gsi::method ("net_for_pin", (db::Net *(db::Circuit::*) (size_t)) &db::Circuit::net_for_pin, gsi::arg ("pin_id"), "@brief Gets the net object attached to a specific pin.\n" "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index f63bdbf78..f4ff086cc 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -144,7 +144,7 @@ static void dump_recursive_nets_to_layout (const db::LayoutToNetlist &l2n, db::L for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { // only handle nets without outgoing pins - these are local - if (c->is_external_net (n.operator-> ())) { + if (n->pin_count () > 0) { continue; } diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 74e4d2acc..0b88052fa 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -165,12 +165,15 @@ static std::string net2string (const db::Net &n) if (! res.empty ()) { res += ","; } - if (i->subcircuit ()) { - res += i->subcircuit ()->circuit_ref () ? i->subcircuit ()->circuit_ref ()->name () : "(null)"; - res += ":"; - } else { - res += "+"; + res += "+"; + res += i->pin () ? i->pin ()->name () : "(null)"; + } + for (db::Net::const_subcircuit_pin_iterator i = n.begin_subcircuit_pins (); i != n.end_subcircuit_pins (); ++i) { + if (! res.empty ()) { + res += ","; } + res += i->subcircuit ()->circuit_ref () ? i->subcircuit ()->circuit_ref ()->name () : "(null)"; + res += ":"; res += i->pin () ? i->pin ()->name () : "(null)"; } return res; @@ -518,25 +521,25 @@ TEST(4_NetlistSubcircuits) c1->add_net (n1a); n1a->set_name ("n1a"); n1a->add_pin (db::NetPinRef (0)); - n1a->add_pin (db::NetPinRef (sc1, 0)); + n1a->add_subcircuit_pin (db::NetSubcircuitPinRef (sc1, 0)); db::Net *n1b = new db::Net (); c1->add_net (n1b); n1b->set_name ("n1b"); - n1b->add_pin (db::NetPinRef (sc1, 1)); - n1b->add_pin (db::NetPinRef (sc2, 0)); + n1b->add_subcircuit_pin (db::NetSubcircuitPinRef (sc1, 1)); + n1b->add_subcircuit_pin (db::NetSubcircuitPinRef (sc2, 0)); db::Net *n1c = new db::Net (); c1->add_net (n1c); n1c->set_name ("n1c"); - n1c->add_pin (db::NetPinRef (sc2, 1)); + n1c->add_subcircuit_pin (db::NetSubcircuitPinRef (sc2, 1)); n1c->add_pin (db::NetPinRef (1)); EXPECT_EQ (nl2string (*nl), "[c1]\n" "+c1p1,c2:c2p1\n" "c2:c2p2,c2:c2p1\n" - "c2:c2p2,+c1p2\n" + "+c1p2,c2:c2p2\n" "[c2]\n" "D:A,+c2p1\n" "D:B,+c2p2\n" @@ -571,7 +574,7 @@ TEST(4_NetlistSubcircuits) "[c1]\n" "+c1p1,c2:c2p1\n" "c2:c2p2,c2:c2p1\n" - "c2:c2p2,+c1p2\n" + "+c1p2,c2:c2p2\n" "[c2]\n" "D:A,+c2p1\n" "D:B,+c2p2\n" @@ -751,24 +754,25 @@ TEST(8_NetSubCircuitsEditing) n2->set_name ("n2"); c.add_net (n2); - EXPECT_EQ (c.is_external_net (n1), false); + EXPECT_EQ (n1->pin_count (), size_t (0)); c.connect_pin (0, n1); EXPECT_EQ (n1->terminal_count (), size_t (0)); EXPECT_EQ (n1->pin_count (), size_t (1)); EXPECT_EQ (n1->is_floating (), true); EXPECT_EQ (n1->is_internal (), false); - EXPECT_EQ (c.is_external_net (n1), true); + EXPECT_NE (n1->pin_count (), size_t (0)); EXPECT_EQ (c.net_for_pin (0), n1); EXPECT_EQ (c.net_for_pin (1), 0); sc1->connect_pin (0, n1); sc1->connect_pin (1, n2); - EXPECT_EQ (c.is_external_net (n2), false); + EXPECT_EQ (n2->pin_count (), size_t (0)); EXPECT_EQ (n1->terminal_count (), size_t (0)); - EXPECT_EQ (n1->pin_count (), size_t (2)); + EXPECT_EQ (n1->pin_count (), size_t (1)); + EXPECT_EQ (n1->subcircuit_pin_count (), size_t (1)); EXPECT_EQ (n1->is_floating (), false); EXPECT_EQ (n1->is_internal (), false); @@ -814,7 +818,7 @@ TEST(8_NetSubCircuitsEditing) EXPECT_EQ (net2string (*n2), "sc2:A"); c.connect_pin (1, n1); - EXPECT_EQ (net2string (*n1), "sc2:B,+Y"); + EXPECT_EQ (net2string (*n1), "+Y,sc2:B"); EXPECT_EQ (c.net_for_pin (1), n1); delete n1; @@ -849,14 +853,14 @@ TEST(10_NetPinRefBasics) { db::SubCircuit d1, d2; - EXPECT_EQ (db::NetPinRef (&d1, 0) == db::NetPinRef (&d1, 0), true); - EXPECT_EQ (db::NetPinRef (&d1, 0) == db::NetPinRef (&d1, 1), false); - EXPECT_EQ (db::NetPinRef (&d1, 0) == db::NetPinRef (&d2, 0), false); + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 0) == db::NetSubcircuitPinRef (&d1, 0), true); + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 0) == db::NetSubcircuitPinRef (&d1, 1), false); + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 0) == db::NetSubcircuitPinRef (&d2, 0), false); - EXPECT_EQ (db::NetPinRef (&d1, 0) < db::NetPinRef (&d1, 0), false); - EXPECT_EQ (db::NetPinRef (&d1, 0) < db::NetPinRef (&d1, 1), true); - EXPECT_EQ (db::NetPinRef (&d1, 1) < db::NetPinRef (&d1, 0), false); - EXPECT_NE ((db::NetPinRef (&d1, 0) < db::NetPinRef (&d2, 0)), (db::NetPinRef (&d2, 0) < db::NetPinRef (&d1, 0))); + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 0) < db::NetSubcircuitPinRef (&d1, 0), false); + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 0) < db::NetSubcircuitPinRef (&d1, 1), true); + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 1) < db::NetSubcircuitPinRef (&d1, 0), false); + EXPECT_NE ((db::NetSubcircuitPinRef (&d1, 0) < db::NetSubcircuitPinRef (&d2, 0)), (db::NetSubcircuitPinRef (&d2, 0) < db::NetSubcircuitPinRef (&d1, 0))); } TEST(11_NetlistCircuitRefs) diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 39c85f047..4f8a3e1c1 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -330,7 +330,8 @@ class DBNetlist_TestClass < TestBase assert_equal(net.is_internal?, false) sc1.connect_pin(1, net) - assert_equal(net.pin_count, 1) + assert_equal(net.pin_count, 0) + assert_equal(net.subcircuit_pin_count, 1) assert_equal(net.terminal_count, 0) assert_equal(net.is_floating?, true) assert_equal(net.is_internal?, false) @@ -338,16 +339,19 @@ class DBNetlist_TestClass < TestBase assert_equal(sc1.net_for_pin(0).inspect, "nil") sc2.connect_pin(0, net) - assert_equal(net.pin_count, 2) + assert_equal(net.pin_count, 0) + assert_equal(net.subcircuit_pin_count, 2) assert_equal(net.terminal_count, 0) assert_equal(net.is_floating?, false) assert_equal(net.is_internal?, false) cnames = [] - net.each_pin { |p| cnames << p.subcircuit.name + ":" + p.pin.name } + net.each_pin { |p| cnames << "+" + p.pin.name } + net.each_subcircuit_pin { |p| cnames << p.subcircuit.name + ":" + p.pin.name } assert_equal(cnames, [ "SC1:B", "SC2:A" ]) cnames = [] - net.each_pin { |p| cnames << p.subcircuit.name + ":" + p.pin_id.to_s } + net.each_pin { |p| cnames << "+" + p.pin.name } + net.each_subcircuit_pin { |p| cnames << p.subcircuit.name + ":" + p.pin_id.to_s } assert_equal(cnames, [ "SC1:1", "SC2:0" ]) net.each_pin { |p| assert_equal(p.net.name, "NET") } @@ -355,9 +359,10 @@ class DBNetlist_TestClass < TestBase assert_equal(sc1.net_for_pin(1).inspect, "nil") cnames = [] - net.each_pin { |p| cnames << p.subcircuit.name + ":" + p.pin.name } + net.each_pin { |p| cnames << "+" + p.pin.name } + net.each_subcircuit_pin { |p| cnames << p.subcircuit.name + ":" + p.pin.name } assert_equal(cnames, [ "SC2:A" ]) - net.each_pin { |p| assert_equal(p.net.name, "NET") } + net.each_subcircuit_pin { |p| assert_equal(p.net.name, "NET") } net.clear assert_equal(sc1.net_for_pin(1).inspect, "nil") @@ -510,9 +515,13 @@ class DBNetlist_TestClass < TestBase c.each_net { |n| names << n.name } assert_equal(names, [ "NET1", "NET2" ]) - assert_equal(c.is_external_net?(net1), false) + assert_equal(net1.pin_count, 0) c.connect_pin(pina1, net1) - assert_equal(c.is_external_net?(net1), true) + cnames = [] + net1.each_pin { |p| cnames << "+" + p.pin.name } + net1.each_subcircuit_pin { |p| cnames << p.subcircuit.name + ":" + p.pin.name } + assert_equal(cnames, [ "+A1" ]) + assert_equal(net1.pin_count, 1) c.connect_pin(pinb1.id, net1) c.connect_pin(pina2, net2) c.connect_pin(pinb2.id, net2) From 64c2548ab8432bc5d345942a5480a2bfb76ea4a5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Jan 2019 15:28:40 +0100 Subject: [PATCH 184/335] WIP: first steps towards global nets. --- src/db/db/db.pro | 3 +- src/db/db/dbHierNetworkProcessor.cc | 54 ++++++++++++++ src/db/db/dbHierNetworkProcessor.h | 29 ++++++++ src/db/db/dbLayoutToNetlist.cc | 21 ++++++ src/db/db/dbLayoutToNetlist.h | 11 +++ src/db/db/dbNetlist.cc | 14 ++++ src/db/db/dbNetlist.h | 31 ++++++++ src/db/db/dbNetlistExtractor.cc | 74 ++++++++++++++++--- src/db/db/dbNetlistExtractor.h | 2 +- src/db/db/gsiDeclDbHierNetworkProcessor.cc | 12 +++ src/db/db/gsiDeclDbLayoutToNetlist.cc | 8 ++ src/db/db/gsiDeclDbNetlist.cc | 62 +++++++++++++++- .../unit_tests/dbHierNetworkProcessorTests.cc | 33 +++++++++ src/db/unit_tests/dbNetlistTests.cc | 51 ++++++++++++- testdata/ruby/dbLayoutToNetlist.rb | 4 + testdata/ruby/dbNetlist.rb | 10 +++ 16 files changed, 406 insertions(+), 13 deletions(-) diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 9d78787ca..9d22a4990 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -269,7 +269,8 @@ HEADERS = \ dbNetlistDeviceExtractor.h \ dbNetlistExtractor.h \ dbNetlistDeviceExtractorClasses.h \ - dbLayoutToNetlist.h + dbLayoutToNetlist.h \ + dbHierNetworkProcessor.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 155e76e92..a8862ef39 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -77,6 +77,60 @@ Connectivity::connect (const db::DeepLayer &la, const db::DeepLayer &lb) connect (la.layer (), lb.layer ()); } +Connectivity::global_nets_type s_empty_global_nets; + +Connectivity::global_nets_iterator +Connectivity::begin_global_connections (unsigned int l) const +{ + std::map::const_iterator g = m_global_connections.find (l); + if (g != m_global_connections.end ()) { + return g->second.begin (); + } else { + return s_empty_global_nets.begin (); + } +} + +Connectivity::global_nets_iterator +Connectivity::end_global_connections (unsigned int l) const +{ + std::map::const_iterator g = m_global_connections.find (l); + if (g != m_global_connections.end ()) { + return g->second.end (); + } else { + return s_empty_global_nets.end (); + } +} + +size_t +Connectivity::connect_global (unsigned int l, const std::string &gn) +{ + for (std::vector::const_iterator i = m_global_net_names.begin (); i != m_global_net_names.end (); ++i) { + if (*i == gn) { + size_t id = i - m_global_net_names.begin (); + m_global_connections [l].insert (id); + return id; + } + } + + size_t id = m_global_net_names.size (); + m_global_connections [l].insert (id); + m_global_net_names.push_back (gn); + return id; +} + +size_t +Connectivity::connect_global (const db::DeepLayer &l, const std::string &gn) +{ + return connect_global (l.layer (), gn); +} + +const std::string & +Connectivity::global_net_name (size_t id) const +{ + tl_assert (id < m_global_net_names.size ()); + return m_global_net_names [id]; +} + Connectivity::layer_iterator Connectivity::begin_layers () const { diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index e1e670b82..ca551c731 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -52,6 +52,8 @@ class DB_PUBLIC Connectivity public: typedef std::set layers_type; typedef layers_type::const_iterator layer_iterator; + typedef std::set global_nets_type; + typedef global_nets_type::const_iterator global_nets_iterator; /** * @brief Creates a connectivity object without any connections @@ -68,6 +70,11 @@ public: */ void connect (unsigned int la, unsigned int lb); + /** + * @brief Adds a connection to a global net + */ + size_t connect_global (unsigned int l, const std::string &gn); + /** * @brief Adds intra-layer connectivity for layer l * This is a convenience method that takes a db::DeepLayer object. @@ -82,6 +89,16 @@ public: */ void connect (const db::DeepLayer &la, const db::DeepLayer &lb); + /** + * @brief Adds a connection to a global net + */ + size_t connect_global (const db::DeepLayer &la, const std::string &gn); + + /** + * @brief Gets the global net name per ID + */ + const std::string &global_net_name (size_t id) const; + /** * @brief Begin iterator for the layers involved */ @@ -102,6 +119,16 @@ public: */ layer_iterator end_connected (unsigned int layer) const; + /** + * @brief Begin iterator for the global connections for a specific layer + */ + global_nets_iterator begin_global_connections (unsigned int layer) const; + + /** + * @brief End iterator for the layers connected to a specific layer + */ + global_nets_iterator end_global_connections (unsigned int layer) const; + /** * @brief Returns true, if the given shapes on the given layers interact * @@ -123,6 +150,8 @@ public: private: layers_type m_all_layers; std::map m_connected; + std::vector m_global_net_names; + std::map m_global_connections; }; /** diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 5a3bcfe8a..2c4a09424 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -149,6 +149,27 @@ void LayoutToNetlist::connect (const db::Region &a, const db::Region &b) m_conn.connect (dla.layer (), dlb.layer ()); } +size_t LayoutToNetlist::connect_global (const db::Region &l, const std::string &gn) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! is_deep (l)) { + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in intra-layer connectivity for netlist extraction")))); + } + + // we need to keep a reference, so we can safely delete the region + db::DeepLayer dl (l); + m_dlrefs.insert (dl); + + return m_conn.connect_global (dl.layer (), gn); +} + +const std::string &LayoutToNetlist::global_net_name (size_t id) const +{ + return m_conn.global_net_name (id); +} + void LayoutToNetlist::extract_netlist () { if (m_netlist_extracted) { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 7e7f40689..c43cac8fb 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -165,6 +165,17 @@ public: */ void connect (const db::Region &a, const db::Region &b); + /** + * @brief Connects the given layer with a global net with the given name + * Returns the global net ID + */ + size_t connect_global (const db::Region &l, const std::string &gn); + + /** + * @brief Gets the global net name for a given global net ID + */ + const std::string &global_net_name (size_t id) const; + /** * @brief Runs the netlist extraction * See the class description for more details. diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 914f6a882..eeb88a042 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -113,8 +113,22 @@ const Net *Device::net_for_terminal (size_t terminal_id) const return 0; } +void Device::connect_terminal_global (size_t terminal_id, size_t global_net_id) +{ + connect_terminal (terminal_id, 0); + m_global_connections.push_back (std::make_pair (terminal_id, global_net_id)); +} + void Device::connect_terminal (size_t terminal_id, Net *net) { + for (size_t i = 0; i < m_global_connections.size (); ) { + if (m_global_connections [i].first == terminal_id) { + m_global_connections.erase (m_global_connections.begin () + i); + } else { + ++i; + } + } + if (net_for_terminal (terminal_id) == net) { return; } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 7d048db5a..3af81c79c 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -806,6 +806,9 @@ class DB_PUBLIC Device : public tl::Object { public: + typedef std::vector > global_connections; + typedef global_connections::const_iterator global_connections_iterator; + /** * @brief Default constructor */ @@ -881,6 +884,31 @@ public: return m_name; } + /** + * @brief Gets the global connections iterator (begin) + * Global connections are terminals attached to a global net. + * This iterator delivers a pair of terminal ID (first) + * and global net ID (second). + * See Connectivity for the definition of the global net ID. + */ + global_connections_iterator begin_global_connections () const + { + return m_global_connections.begin (); + } + + /** + * @brief Gets the global connections iterator (end) + */ + global_connections_iterator end_global_connections () const + { + return m_global_connections.end (); + } + + /** + * @brief Connects the given terminal to the given global net + */ + void connect_terminal_global (size_t terminal_id, size_t global_net_id); + /** * @brief Gets the net attached to a specific terminal * Returns 0 if no net is attached. @@ -901,6 +929,8 @@ public: * If the net is 0 the terminal is disconnected. * If non-null, a NetTerminalRef object will be inserted into the * net and connected with the given terminal. + * If the terminal is connected to a global net, it will be + * disconnected from there. */ void connect_terminal (size_t terminal_id, Net *net); @@ -936,6 +966,7 @@ private: std::vector m_parameters; size_t m_id; Circuit *mp_circuit; + global_connections m_global_connections; /** * @brief Sets the terminal reference for a specific terminal diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 6dfd810eb..02fcdaa0f 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -64,7 +64,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); } - std::map > pins_per_cluster; + std::map > pins_per_cluster_per_cell; + std::map > global_nets_per_cell; for (db::Layout::bottom_up_const_iterator cid = layout.begin_bottom_up (); cid != layout.end_bottom_up (); ++cid) { @@ -88,7 +89,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect circuit = k->second; } - std::map &c2p = pins_per_cluster [*cid]; + std::map &global_nets = global_nets_per_cell [*cid]; + std::map &c2p = pins_per_cluster_per_cell [*cid]; std::map subcircuits; @@ -98,8 +100,19 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect net->set_cluster_id (*c); circuit->add_net (net); + // make global net connections for clusters which connect to such + std::set global_net_ids; + std::vector layers = clusters.cluster_by_id (*c).layers (); + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + global_net_ids.insert (conn.begin_global_connections (*l), conn.end_global_connections (*l)); + } + for (std::set::const_iterator g = global_net_ids.begin (); g != global_net_ids.end (); ++g) { + global_nets.insert (std::make_pair (*g, net)); + assign_net_name (conn.global_net_name (*g), net); + } + // make subcircuit connections (also make the subcircuits if required) from the connections of the clusters - make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster, layout.dbu ()); + make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell, layout.dbu ()); // collect the properties - we know that the cluster attributes are property ID's because the // cluster processor converts shape property IDs to attributes @@ -110,7 +123,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect if (terminal_annot_name_id.first && j->first == terminal_annot_name_id.second) { make_device_terminal_from_property (j->second, circuit, net); } else if (text_annot_name_id.first && j->first == text_annot_name_id.second) { - make_net_name_from_property (j->second, net); + assign_net_name (j->second.to_string (), net); } } } @@ -123,6 +136,49 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } + // make global net connections for devices + for (db::Circuit::device_iterator d = circuit->begin_devices (); d != circuit->end_devices (); ++d) { + + for (db::Device::global_connections_iterator g = d->begin_global_connections (); g != d->end_global_connections (); ++g) { + + db::Net *&net = global_nets [g->second]; + if (! net) { + net = new db::Net (conn.global_net_name (g->second)); + circuit->add_net (net); + } + + net->add_terminal (db::NetTerminalRef (d.operator-> (), g->first)); + + } + + } + + // make the global net connections into subcircuits - if necessary by creating pins into the subcircuit + for (db::Circuit::subcircuit_iterator sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) { + + db::Circuit *subcircuit = sc->circuit_ref (); + + const std::map &sc_gn = global_nets_per_cell [subcircuit->cell_index ()]; + for (std::map::const_iterator g = global_nets.begin (); g != global_nets.end (); ++g) { + + std::map::const_iterator gg = sc_gn.find (g->first); + if (gg != sc_gn.end ()) { + + size_t pin_id = 0; + if (gg->second->pin_count () > 0) { + pin_id = gg->second->begin_pins ()->pin_id (); + } else { + pin_id = subcircuit->add_pin (conn.global_net_name (gg->first)).id (); + subcircuit->connect_pin (pin_id, gg->second); + } + g->second->add_subcircuit_pin (db::NetSubcircuitPinRef (sc.operator-> (), pin_id)); + + } + + } + + } + } } @@ -192,14 +248,14 @@ void NetlistExtractor::make_device_terminal_from_property (const tl::Variant &v, } } -void NetlistExtractor::make_net_name_from_property (const tl::Variant &v, db::Net *net) +void NetlistExtractor::assign_net_name (const std::string &n, db::Net *net) { - std::string n = v.to_string (); - if (! n.empty ()) { + std::string nn = n; + if (! nn.empty ()) { if (! net->name ().empty ()) { - n = net->name () + "," + n; + nn = net->name () + "," + nn; } - net->set_name (n); + net->set_name (nn); } } diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index b95953ecf..2e87113c8 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -95,7 +95,7 @@ private: hier_clusters_type m_net_clusters; void make_device_terminal_from_property (const tl::Variant &v, db::Circuit *circuit, db::Net *net); - void make_net_name_from_property (const tl::Variant &v, db::Net *net); + void assign_net_name (const std::string &n, db::Net *net); /** * @brief Make a pin connection from clusters diff --git a/src/db/db/gsiDeclDbHierNetworkProcessor.cc b/src/db/db/gsiDeclDbHierNetworkProcessor.cc index 4514f0084..3537bf7e1 100644 --- a/src/db/db/gsiDeclDbHierNetworkProcessor.cc +++ b/src/db/db/gsiDeclDbHierNetworkProcessor.cc @@ -32,6 +32,13 @@ Class decl_dbConnectivity ("db", "Connectivity", ) + gsi::method ("connect", (void (db::Connectivity::*) (unsigned int, unsigned int)) &db::Connectivity::connect, gsi::arg ("layer_a"), gsi::arg ("layer_b"), "@brief Specifies inter-layer connectivity.\n" + ) + + gsi::method ("connect_global", (size_t (db::Connectivity::*) (unsigned int, const std::string &)) &db::Connectivity::connect_global, gsi::arg ("layer"), gsi::arg ("global_net_name"), + "@brief Connects the given layer to the global net given by name.\n" + "Returns the ID of the global net." + ) + + gsi::method ("global", &db::Connectivity::global_net_name, gsi::arg ("global_net_id"), + "@brief Gets the name for a given global net ID.\n" ), "@brief This class specifies connections between different layers." "Connections are build using \\connect. There are basically two flavours of connections: intra-layer and inter-layer.\n" @@ -45,6 +52,11 @@ Class decl_dbConnectivity ("db", "Connectivity", "\n" "All layers are specified in terms of layer indexes. Layer indexes are layout layer indexes (see \\Layout class).\n" "\n" + "The connectivity object also manages the global nets. Global nets are substrate for example " + "and they are propagated automatically from subcircuits to circuits. " + "Global nets are defined by name and are managed through IDs. To get the name for a given ID, use " + "\\global_net_name." + "\n" "This class has been introduced in version 0.26.\n" ); diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index a9e50a9be..6b7d4ea0d 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -123,6 +123,14 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "@brief Defines an inter-layer connection for the given layers.\n" "The conditions mentioned with intra-layer \\connect apply for this method too.\n" ) + + gsi::method ("connect_global", (void (db::LayoutToNetlist::*) (const db::Region &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"), + "@brief Defines a connection of the given layer with a global net.\n" + "This method returns the ID of the global net. Use \\global_net_name to get " + "the name back from the ID." + ) + + gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"), + "@brief Gets the global net name for the given global net ID." + ) + gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, "@brief Runs the netlist extraction\n" "See the class description for more details.\n" diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 86b357b54..716f909fd 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -61,6 +61,44 @@ static void device_disconnect_terminal_by_name (db::Device *device, const std::s device_connect_terminal_by_name (device, terminal_name, 0); } +static tl::Variant device_terminal_for_global_net (const db::Device *device, size_t global_net) +{ + for (db::Device::global_connections_iterator g = device->begin_global_connections (); g != device->end_global_connections (); ++g) { + if (g->second == global_net) { + return tl::Variant (g->first); + } + } + return tl::Variant (); +} + +static tl::Variant device_global_net_for_terminal (const db::Device *device, size_t terminal_id) +{ + for (db::Device::global_connections_iterator g = device->begin_global_connections (); g != device->end_global_connections (); ++g) { + if (g->first == terminal_id) { + return tl::Variant (g->second); + } + } + return tl::Variant (); +} + +static tl::Variant device_global_net_for_terminal_name (const db::Device *device, const std::string &terminal_name) +{ + if (! device->device_class ()) { + throw tl::Exception (tl::to_string (tr ("Device does not have a device class"))); + } + size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name); + return device_global_net_for_terminal (device, terminal_id); +} + +static void device_connect_terminal_global_by_name (db::Device *device, const std::string &terminal_name, size_t global_net) +{ + if (! device->device_class ()) { + throw tl::Exception (tl::to_string (tr ("Device does not have a device class"))); + } + size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name); + device->connect_terminal_global (terminal_id, global_net); +} + Class decl_dbDevice ("db", "Device", gsi::method ("device_class", &db::Device::device_class, "@brief Gets the device class the device belongs to.\n" @@ -91,15 +129,37 @@ Class decl_dbDevice ("db", "Device", ) + gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal, gsi::arg ("terminal_id"), "@brief Disconnects the given terminal from any net.\n" + "If the terminal has been connected to a global, this connection will be disconnected too." ) + gsi::method_ext ("connect_terminal", &device_connect_terminal_by_name, gsi::arg ("terminal_name"), gsi::arg ("net"), "@brief Connects the given terminal to the specified net.\n" - "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised." + "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised.\n" + "If the terminal has been connected to a global net, it will be disconnected from there." ) + gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal_by_name, gsi::arg ("terminal_name"), "@brief Disconnects the given terminal from any net.\n" "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised." ) + + gsi::method ("connect_terminal_global", &db::Device::connect_terminal_global, gsi::arg ("terminal_id"), gsi::arg ("global_net_id"), + "@brief Connects the given terminal to the given global net.\n" + "The global net ID is taken from \\Connectivity (connect_global, etc.).\n" + "If the terminal was already connected to another net, it will be disconnected from there." + ) + + gsi::method_ext ("connect_terminal_global", &device_connect_terminal_global_by_name, gsi::arg ("terminal_name"), gsi::arg ("global_net_id"), + "@brief Connects the given terminal to the given global net.\n" + "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised." + ) + + gsi::method_ext ("terminal_on_global_net", &device_terminal_for_global_net, gsi::arg ("global_net_id"), + "@brief Gets the terminal ID for the given global net or nil if no terminal is not that global net.\n" + "The global net ID is managed by the \\Connectivity object." + ) + + gsi::method_ext ("global_net_on_terminal", &device_global_net_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the global net ID for the given terminal ID or nil if the terminal is not connected to a global net.\n" + ) + + gsi::method_ext ("global_net_on_terminal", &device_global_net_for_terminal_name, gsi::arg ("terminal_name"), + "@brief Gets the global net ID for the given terminal name or nil if the terminal is not connected to a global net.\n" + "If the name is not a valid terminal name, an exception is raised." + ) + gsi::method ("parameter", (double (db::Device::*) (size_t) const) &db::Device::parameter_value, gsi::arg ("param_id"), "@brief Gets the parameter value for the given parameter ID." ) + diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index a16a73294..96c7d7563 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -44,6 +44,18 @@ static std::string l2s (db::Connectivity::layer_iterator b, db::Connectivity::la return s; } +static std::string gn2s (db::Connectivity::global_nets_iterator b, db::Connectivity::global_nets_iterator e) +{ + std::string s; + for (db::Connectivity::global_nets_iterator i = b; i != e; ++i) { + if (! s.empty ()) { + s += ","; + } + s += tl::to_string (*i); + } + return s; +} + TEST(1_Connectivity) { db::Connectivity conn; @@ -69,6 +81,27 @@ TEST(1_Connectivity) EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1,2"); EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0,1"); EXPECT_EQ (l2s (conn.begin_connected (2), conn.end_connected (2)), "0,2"); + + EXPECT_EQ (conn.connect_global (0, "GLOBAL"), size_t (0)); + EXPECT_EQ (gn2s (conn.begin_global_connections (2), conn.end_global_connections (2)), ""); + EXPECT_EQ (gn2s (conn.begin_global_connections (0), conn.end_global_connections (0)), "0"); + EXPECT_EQ (conn.connect_global (2, "GLOBAL2"), size_t (1)); + EXPECT_EQ (gn2s (conn.begin_global_connections (2), conn.end_global_connections (2)), "1"); + EXPECT_EQ (conn.connect_global (0, "GLOBAL2"), size_t (1)); + EXPECT_EQ (gn2s (conn.begin_global_connections (0), conn.end_global_connections (0)), "0,1"); + + EXPECT_EQ (conn.global_net_name (0), "GLOBAL"); + EXPECT_EQ (conn.global_net_name (1), "GLOBAL2"); + + db::Connectivity conn2 = conn; + + EXPECT_EQ (l2s (conn2.begin_connected (0), conn2.end_connected (0)), "0,1,2"); + EXPECT_EQ (l2s (conn2.begin_connected (1), conn2.end_connected (1)), "0,1"); + EXPECT_EQ (l2s (conn2.begin_connected (2), conn2.end_connected (2)), "0,2"); + + EXPECT_EQ (gn2s (conn2.begin_global_connections (0), conn2.end_global_connections (0)), "0,1"); + EXPECT_EQ (conn2.global_net_name (0), "GLOBAL"); + EXPECT_EQ (conn2.global_net_name (1), "GLOBAL2"); } TEST(2_ShapeInteractions) diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 0b88052fa..08ede9d34 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -74,7 +74,6 @@ TEST(1_DeviceTerminalDefinition) dc.clear_parameter_definitions (); EXPECT_EQ (dc.parameter_definitions ().empty (), true); - } TEST(2_DeviceClass) @@ -1025,3 +1024,53 @@ TEST(12_NetlistTopology) EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2"); EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1"); } + +TEST(13_DeviceGlobalNets) +{ + db::DeviceTerminalDefinition pd; + pd.set_name ("name"); + pd.set_description ("nothing yet"); + + db::DeviceTerminalDefinition pd2; + pd2.set_name ("name2"); + pd2.set_description ("now it has something"); + + db::DeviceClass dc; + dc.set_name ("devname"); + dc.set_description ("devdesc"); + dc.add_terminal_definition (pd); + dc.add_terminal_definition (pd2); + + db::Device d (&dc); + db::Net n; + + d.connect_terminal_global (0, 17); + + db::Device::global_connections_iterator g; + + g = d.begin_global_connections (); + EXPECT_EQ (g != d.end_global_connections (), true); + EXPECT_EQ (g->first, size_t (0)); + EXPECT_EQ (g->second, size_t (17)); + + ++g; + EXPECT_EQ (g == d.end_global_connections (), true); + + d.connect_terminal (0, &n); + g = d.begin_global_connections (); + EXPECT_EQ (g == d.end_global_connections (), true); + EXPECT_EQ (d.net_for_terminal (0) == &n, true); + + d.connect_terminal_global (0, 17); + EXPECT_EQ (d.net_for_terminal (0) == 0, true); + + g = d.begin_global_connections (); + EXPECT_EQ (g != d.end_global_connections (), true); + EXPECT_EQ (g->first, size_t (0)); + EXPECT_EQ (g->second, size_t (17)); + + d.connect_terminal (0, 0); + g = d.begin_global_connections (); + EXPECT_EQ (g == d.end_global_connections (), true); + EXPECT_EQ (d.net_for_terminal (0) == 0, true); +} diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index d1a37ef11..3ca02cc67 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -61,6 +61,10 @@ class DBLayoutToNetlist_TestClass < TestBase assert_equal(l2n.internal_layout.cell(ci).name, ly2.cell(cm.cell_mapping(ci)).name) end + rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) ) + bulk_id = l2n.connect_global(rmetal1, "BULK") + assert_equal(l2n.global_net_name(bulk_id), "BULK") + end def test_2_ShapesFromNet diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 4f8a3e1c1..86e1954c7 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -257,6 +257,16 @@ class DBNetlist_TestClass < TestBase assert_equal(d1.net_for_terminal(1).inspect, "nil") assert_equal(d1.net_for_terminal(0).inspect, "nil") + d2.connect_terminal(0, net) + assert_equal(net.terminal_count, 1) + d2.connect_terminal_global(0, 1) + assert_equal(net.terminal_count, 0) + + assert_equal(d2.terminal_on_global_net(1), 0) + assert_equal(d2.terminal_on_global_net(17).inspect, "nil") + assert_equal(d2.global_net_on_terminal(0), 1) + assert_equal(d2.global_net_on_terminal(1).inspect, "nil") + end def test_5_SubCircuit From 6cf7558384fce0984ec07acffbf9561328bd1fee Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Jan 2019 17:04:13 +0100 Subject: [PATCH 185/335] WIP: preparations for global net extraction --- src/db/db/dbHierNetworkProcessor.cc | 105 +++++++++++++----- src/db/db/dbHierNetworkProcessor.h | 25 +++++ src/db/db/dbNetlistExtractor.cc | 36 ++++-- .../unit_tests/dbHierNetworkProcessorTests.cc | 72 ++++++++++++ 4 files changed, 203 insertions(+), 35 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index a8862ef39..dccedd970 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -227,6 +227,13 @@ local_cluster::clear () m_attrs.clear (); } +template +void +local_cluster::set_global_nets (const global_nets &gn) +{ + m_global_nets = gn; +} + template void local_cluster::add_attr (attr_id a) @@ -667,6 +674,9 @@ struct cluster_building_receiver { typedef typename local_cluster::id_type id_type; typedef std::pair > shape_value; + typedef std::vector shape_vector; + typedef std::set global_nets; + typedef std::pair cluster_value; cluster_building_receiver (const db::Connectivity &conn) : mp_conn (&conn) @@ -677,15 +687,17 @@ struct cluster_building_receiver void generate_clusters (local_clusters &clusters) { // build the resulting clusters - for (typename std::list >::const_iterator c = m_clusters.begin (); c != m_clusters.end (); ++c) { + for (typename std::list::const_iterator c = m_clusters.begin (); c != m_clusters.end (); ++c) { // TODO: reserve? local_cluster *cluster = clusters.insert (); - for (typename std::vector::const_iterator s = c->begin (); s != c->end (); ++s) { + for (typename shape_vector::const_iterator s = c->first.begin (); s != c->first.end (); ++s) { cluster->add (*s->first, s->second.first); cluster->add_attr (s->second.second); } + cluster->set_global_nets (c->second); + } } @@ -695,69 +707,106 @@ struct cluster_building_receiver return; } - typename std::map >::iterator>::iterator ic1 = m_shape_to_clusters.find (s1); - typename std::map >::iterator>::iterator ic2 = m_shape_to_clusters.find (s2); + typename std::map::iterator>::iterator ic1 = m_shape_to_clusters.find (s1); + typename std::map::iterator>::iterator ic2 = m_shape_to_clusters.find (s2); if (ic1 == m_shape_to_clusters.end ()) { if (ic2 == m_shape_to_clusters.end ()) { - m_clusters.push_back (std::vector ()); - typename std::list >::iterator c = --m_clusters.end (); - c->push_back (std::make_pair (s1, p1)); - c->push_back (std::make_pair (s2, p2)); + m_clusters.push_back (cluster_value ()); + typename std::list::iterator c = --m_clusters.end (); + c->first.push_back (std::make_pair (s1, p1)); + c->first.push_back (std::make_pair (s2, p2)); m_shape_to_clusters.insert (std::make_pair (s1, c)); m_shape_to_clusters.insert (std::make_pair (s2, c)); } else { - ic2->second->push_back (std::make_pair (s1, p1)); + ic2->second->first.push_back (std::make_pair (s1, p1)); m_shape_to_clusters.insert (std::make_pair (s1, ic2->second)); } } else if (ic2 == m_shape_to_clusters.end ()) { - ic1->second->push_back (std::make_pair (s2, p2)); + ic1->second->first.push_back (std::make_pair (s2, p2)); m_shape_to_clusters.insert (std::make_pair (s2, ic1->second)); } else if (ic1->second != ic2->second) { // join clusters: use the larger one as the target - if (ic1->second->size () < ic2->second->size ()) { - std::swap (ic1, ic2); + if (ic1->second->first.size () < ic2->second->first.size ()) { + join (ic2->second, ic1->second); + } else { + join (ic1->second, ic2->second); } - ic1->second->insert (ic1->second->end (), ic2->second->begin (), ic2->second->end ()); - - typename std::list >::iterator j = ic2->second; - for (typename std::vector::const_iterator i = j->begin (); i != j->end (); ++i) { - m_shape_to_clusters [i->first] = ic1->second; - } - - m_clusters.erase (j); - } } void finish (const T *s, std::pair p) { // if the shape has not been handled yet, insert a single cluster with only this shape - typename std::map >::iterator>::const_iterator ic = m_shape_to_clusters.find (s); + typename std::map::iterator>::iterator ic = m_shape_to_clusters.find (s); if (ic == m_shape_to_clusters.end ()) { - m_clusters.push_back (std::vector ()); - typename std::list >::iterator c = --m_clusters.end (); - c->push_back (std::make_pair (s, p)); - m_shape_to_clusters.insert (std::make_pair (s, c)); + + m_clusters.push_back (cluster_value ()); + typename std::list::iterator c = --m_clusters.end (); + c->first.push_back (std::make_pair (s, p)); + + ic = m_shape_to_clusters.insert (std::make_pair (s, c)).first; + + } + + // consider connections to global nets + + db::Connectivity::global_nets_iterator ge = mp_conn->end_global_connections (p.first); + for (db::Connectivity::global_nets_iterator g = mp_conn->begin_global_connections (p.first); g != ge; ++g) { + + typename std::map::iterator>::iterator icg = m_global_to_clusters.find (*g); + + if (icg == m_global_to_clusters.end ()) { + + ic->second->second.insert (*g); + m_global_to_clusters.insert (std::make_pair (*g, ic->second)); + + } else if (ic->second != icg->second) { + + // join clusters + if (ic->second->first.size () < icg->second->first.size ()) { + join (icg->second, ic->second); + } else { + join (ic->second, icg->second); + } + + } + } } private: const db::Connectivity *mp_conn; - std::map >::iterator> m_shape_to_clusters; - std::list > m_clusters; + std::map::iterator> m_shape_to_clusters; + std::map::iterator> m_global_to_clusters; + std::list m_clusters; + + void join (typename std::list::iterator ic1, typename std::list::iterator ic2) + { + ic1->first.insert (ic1->first.end (), ic2->first.begin (), ic2->first.end ()); + ic1->second.insert (ic2->second.begin (), ic2->second.end ()); + + for (typename shape_vector::const_iterator i = ic2->first.begin (); i != ic2->first.end (); ++i) { + m_shape_to_clusters [i->first] = ic1; + } + for (typename global_nets::const_iterator i = ic2->second.begin (); i != ic2->second.end (); ++i) { + m_global_to_clusters [*i] = ic1; + } + + m_clusters.erase (ic2); + } }; } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index ca551c731..e1bfe3746 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -171,6 +171,9 @@ public: typedef size_t attr_id; typedef std::set attr_set; typedef attr_set::const_iterator attr_iterator; + typedef size_t global_net_id; + typedef std::set global_nets; + typedef global_nets::const_iterator global_nets_iterator; /** * @brief Creates an empty cluster @@ -287,6 +290,27 @@ public: return m_attrs.end (); } + /** + * @brief Gets the global net IDs (begin) + */ + global_nets_iterator begin_global_nets () const + { + return m_global_nets.begin (); + } + + /** + * @brief Gets the global net IDs (end) + */ + global_nets_iterator end_global_nets () const + { + return m_global_nets.end (); + } + + /** + * @brief Sets the global nets + */ + void set_global_nets (const global_nets &gn); + private: template friend class local_clusters; template friend class interaction_receiver; @@ -310,6 +334,7 @@ private: std::map m_shapes; box_type m_bbox; attr_set m_attrs; + global_nets m_global_nets; size_t m_size; }; diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 02fcdaa0f..7fe733bbf 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -100,13 +100,11 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect net->set_cluster_id (*c); circuit->add_net (net); - // make global net connections for clusters which connect to such - std::set global_net_ids; - std::vector layers = clusters.cluster_by_id (*c).layers (); - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - global_net_ids.insert (conn.begin_global_connections (*l), conn.end_global_connections (*l)); - } - for (std::set::const_iterator g = global_net_ids.begin (); g != global_net_ids.end (); ++g) { + const db::local_cluster &cluster = clusters.cluster_by_id (*c); + + // collect global net assignments from clusters + for (std::set::const_iterator g = cluster.begin_global_nets (); g != cluster.end_global_nets (); ++g) { + tl_assert (global_nets.find (*g) == global_nets.end ()); global_nets.insert (std::make_pair (*g, net)); assign_net_name (conn.global_net_name (*g), net); } @@ -153,6 +151,30 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } + // if any of the subcircuits has global nets which this circuit doesn't have, propagate them + + std::set seen; + std::set global_nets_of_subcircuits; + for (db::Circuit::subcircuit_iterator sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) { + + db::Circuit *subcircuit = sc->circuit_ref (); + if (seen.find (subcircuit) == seen.end ()) { + + seen.insert (subcircuit); + + const std::map &sc_gn = global_nets_per_cell [subcircuit->cell_index ()]; + for (std::map::const_iterator g = sc_gn.begin (); g != sc_gn.end (); ++g) { + global_nets_of_subcircuits.insert (g->first); + } + + } + + } + + for (std::set::const_iterator g = global_nets_of_subcircuits.begin (); g != global_nets_of_subcircuits.end (); ++g) { + + } + // make the global net connections into subcircuits - if necessary by creating pins into the subcircuit for (db::Circuit::subcircuit_iterator sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) { diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 96c7d7563..8890780a6 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -292,6 +292,9 @@ static std::string local_cluster_to_string (const db::local_cluster &cluster, for (typename db::local_cluster::attr_iterator a = cluster.begin_attr (); a != cluster.end_attr (); ++a) { res += "%" + tl::to_string (*a); } + for (typename db::local_cluster::global_nets_iterator g = cluster.begin_global_nets (); g != cluster.end_global_nets (); ++g) { + res += "+" + conn.global_net_name (*g); + } return res; } @@ -462,6 +465,75 @@ TEST(21_LocalClustersBasicWithAttributes) ); } +TEST(22_LocalClustersWithGlobal) +{ + db::Layout layout; + db::Cell &cell = layout.cell (layout.add_cell ("TOP")); + db::GenericRepository &repo = layout.shape_repository (); + + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + conn.connect (0, 1); + conn.connect (0, 2); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + cell.shapes (0).insert (db::PolygonRef (poly, repo)); + + db::local_clusters clusters; + EXPECT_EQ (local_clusters_to_string (clusters, conn), ""); + + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0)"); + + // one more shape + cell.shapes (0).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (10, 20))), repo), 1)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1"); + + // one more shape creating a new cluster + cell.shapes (2).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1100))), repo), 2)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1\n" + "#2:[2](0,1100;0,2100;1000,2100;1000,1100)%2" + ); + + conn.connect_global (0, "GLOBAL"); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1+GLOBAL\n" + "#2:[2](0,1100;0,2100;1000,2100;1000,1100)%2" + ); + + conn.connect_global (2, "GLOBAL2"); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1+GLOBAL\n" + "#2:[2](0,1100;0,2100;1000,2100;1000,1100)%2+GLOBAL2" + ); + + conn.connect_global (0, "GLOBAL2"); + + // now, GLOBAL2 will connect these clusters + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20);[2](0,1100;0,2100;1000,2100;1000,1100)%1%2+GLOBAL+GLOBAL2" + ); +} + TEST(30_LocalConnectedClusters) { db::Layout layout; From a4f0fd665e648ac4f03f8074830eb5a904cd78e5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Jan 2019 17:50:51 +0100 Subject: [PATCH 186/335] Provided a solution for connectivity through global nets. --- src/db/unit_tests/dbLayoutToNetlistTests.cc | 306 ++++++++++++++++-- .../algo/device_extract_au3_with_rec_nets.gds | Bin 0 -> 49126 bytes testdata/algo/device_extract_l3.gds | Bin 0 -> 4554 bytes 3 files changed, 288 insertions(+), 18 deletions(-) create mode 100644 testdata/algo/device_extract_au3_with_rec_nets.gds create mode 100644 testdata/algo/device_extract_l3.gds diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index f4ff086cc..e631f9b26 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -344,6 +344,15 @@ TEST(1_Basic) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + // compare netlist as string EXPECT_EQ (l2n.netlist ()->to_string (), "Circuit RINGO ():\n" @@ -519,15 +528,6 @@ TEST(1_Basic) " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" ); - // compare the collected test data - - std::string au = tl::testsrc (); - au = tl::combine_path (au, "testdata"); - au = tl::combine_path (au, "algo"); - au = tl::combine_path (au, "device_extract_au1_with_rec_nets.gds"); - - db::compare_layouts (_this, ly, au); - // do some probing after purging // top level @@ -723,6 +723,15 @@ TEST(2_Probing) "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" ); + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au2_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + // do some probing before purging // top level @@ -761,15 +770,6 @@ TEST(2_Probing) " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" ); - // compare the collected test data - - std::string au = tl::testsrc (); - au = tl::combine_path (au, "testdata"); - au = tl::combine_path (au, "algo"); - au = tl::combine_path (au, "device_extract_au2_with_rec_nets.gds"); - - db::compare_layouts (_this, ly, au); - // do some probing after purging // top level @@ -784,3 +784,273 @@ TEST(2_Probing) EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I7"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I3"); } + +TEST(3_GlobalNetConnections) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int pplus = define_layer (ly, lmap, 10); + unsigned int nplus = define_layer (ly, lmap, 11); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l3.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell)); + std::auto_ptr ractive (l2n.make_layer (active)); + std::auto_ptr rpplus (l2n.make_layer (pplus)); + std::auto_ptr rnplus (l2n.make_layer (nplus)); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell & *rpplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell & *rnplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion + unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie + unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpsd.insert_into (&ly, tc.cell_index (), lptie); + rnsd.insert_into (&ly, tc.cell_index (), lntie); + + // NOTE: the device extractor will add more debug layers for the transistors: + // 20/0 -> Diffusion + // 21/0 -> Gate + MOSFETExtractor pmos_ex ("PMOS", &ly); + MOSFETExtractor nmos_ex ("NMOS", &ly); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rdiff_cont, rptie); + l2n.connect (*rdiff_cont, rntie); + l2n.connect (*rnwell, rntie); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 201/0 -> Well + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + // 212/0 -> N tie + // 213/0 -> P tie + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2PAIR $1 ($1=VSS,$2=VSS,$3=FB,$4=VDD,$5=VSS,$6=$I7,$7=OSC,$8=VDD)\n" + " XINV2PAIR $2 ($1=VSS,$2=VSS,$3=$I22,$4=VDD,$5=VSS,$6=FB,$7=$I13,$8=VDD)\n" + " XINV2PAIR $3 ($1=VSS,$2=VSS,$3=$I23,$4=VDD,$5=VSS,$6=$I13,$7=$I5,$8=VDD)\n" + " XINV2PAIR $4 ($1=VSS,$2=VSS,$3=$I24,$4=VDD,$5=VSS,$6=$I5,$7=$I6,$8=VDD)\n" + " XINV2PAIR $5 ($1=VSS,$2=VSS,$3=$I25,$4=VDD,$5=VSS,$6=$I6,$7=$I7,$8=VDD)\n" + "Circuit INV2PAIR ($1=$I10,$2=$I9,$3=$I8,$4=$I6,$5=$I5,$6=$I3,$7=$I2,$8=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I4,$3=$I8,BULK=$I10,OUT=$I2,VSS=$I5,VDD=$I6)\n" + " XINV2 $2 ($1=$I1,IN=$I3,$3=$I7,BULK=$I9,OUT=$I4,VSS=$I5,VDD=$I6)\n" + "Circuit INV2 ($1=$1,IN=IN,$3=$3,BULK=BULK,OUT=OUT,VSS=VSS,VDD=VDD):\n" + " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " XTRANS $1 ($1=$3,$2=VSS,$3=IN)\n" + " XTRANS $2 ($1=$3,$2=VDD,$3=IN)\n" + " XTRANS $3 ($1=VDD,$2=OUT,$3=$3)\n" + " XTRANS $4 ($1=VSS,$2=OUT,$3=$3)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au3_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + + // do some probing before purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I22"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,VSS=VSS):\n" + " XINV2PAIR $1 ($1=VSS,$2=VSS,$3=FB,$4=VDD,$5=VSS,$6=$I7,$7=OSC,$8=VDD)\n" + " XINV2PAIR $2 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=FB,$7=$I13,$8=VDD)\n" + " XINV2PAIR $3 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I13,$7=$I5,$8=VDD)\n" + " XINV2PAIR $4 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I5,$7=$I6,$8=VDD)\n" + " XINV2PAIR $5 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I6,$7=$I7,$8=VDD)\n" + "Circuit INV2PAIR ($1=$I10,$2=$I9,$3=$I8,$4=$I6,$5=$I5,$6=$I3,$7=$I2,$8=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I4,$3=$I8,BULK=$I10,OUT=$I2,VSS=$I5,VDD=$I6)\n" + " XINV2 $2 ($1=$I1,IN=$I3,$3=(null),BULK=$I9,OUT=$I4,VSS=$I5,VDD=$I6)\n" + "Circuit INV2 ($1=(null),IN=IN,$3=$3,BULK=(null),OUT=OUT,VSS=VSS,VDD=VDD):\n" + " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + ); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + // the transistor which supplies this probe target has been optimized away by "purge". + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I8"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); +} diff --git a/testdata/algo/device_extract_au3_with_rec_nets.gds b/testdata/algo/device_extract_au3_with_rec_nets.gds new file mode 100644 index 0000000000000000000000000000000000000000..9ce7a8522f323e714516ce1c2ea0cbb35cef025e GIT binary patch literal 49126 zcmd6wd#qhoedqUmqq=5Nu0z<3^>R*&h;x1v70zf{ETC4Hw0tHZeT`e1XTnG zp;166xKd{dBSg@&qA5aYhN-3xWgx>ug+?f#is99WWU8tJlu6LcsMDaR^p7Dpx%2z1 z-}-v3b?)8k+;dJww@B6}{MP#J-+KP`UVE*(Pt$2eNAGRckBm;d)QmKp=F(NXTNNG-F!#NZVdindAsyh##89+-%j+2vKw>#4|D&`^XC7&UE_Jv zJ=ip(`$X9T`>*ef^VQd`HERF9>zw_v@u$?+DSLZ;(NSOL$WCg5-YCir`|00(%Evp@bH$8$V0QMG zM113g4_WCxn<)D)Tkmi-52c?;K5ZlNw`ONW*P^5_I zx&I%e@oGAIk{bKh&hdMU_CzJhZp`(Xk>Lr_o(`@4HpY#rw)HJbjt|hO*cC+vfIZB8+o_r>h5uC{Ece0_E*B z$3NqpdB!-$ztjFZ*kMiY^HuW{J@ZvhUf8?lYu4XZ?nkZsUvN%*)MCvito)I(cfQbf zdvClkwfBEdiXG4*_VH$sPBKv^*jkpO!^)6(x3SqR+N2Wve0KM z2J6euHO&fpf`%(?#nAj#qU&FMJ4-j|nmfG4U<&%6!&e=!c! z?y%FoqBH%vQLEWBcgPWYg0=r`x~y|#_tq^lJ2t<|aPyAMx1Hes?!3_~x9zk4+0ywLa4?sMDC+Wf&vyM@N`b_-3|+HM|S7COIs zx7)Ea-qMP?tKJ?DJPy!r9{)Y=TfHnkbj5>ApY9X7Z`7VB=eC=VwI(=M6pI=y^w!y}jMku1>9RMf7>Q&uuqLxAE56Zheh6@Bf9|4|=rQ zl1?YQEwa(Q#De6i{Qc359$ms5iKBl`o|y&Y*)}&%jH06{`q(V8J5rcMAGHz2Q_;T_ zWrrTUraTIcMSbTQV&UWpi-par%^G52)A;iV|24Luh zw1@0?$F;g$e1~aW{hux3*{uw&_&+wQo=TM6nBD1y*6-@}&pQMBkN?X3i?SPY|HJBU z$o((>U)IuZv8OEK(!aO5|Bpo3jk*6qzvP%jkdIiojZ3WOR=h7!c4Mm^ztj^_Z~Tk0 zch$e}kB)wV^dEXr_OAY$qqi{^^qpH>FUoGLmfs?A&|kXM^`h*?T)#DM|0!EFE&o^6 zhQ<|lTHDPe%5JRcr~l6N%f9A%QFdch&q@vdtlaP~%3iMEM&^`%#KsN!(Z6!ND7!KD ze`8+%CI8;Ee_=W!|CW9zQFddlpNabLPG{=dzJi9V92tiq<8NxE{fnIbyruI_G?MYU z^hvWtvf_@{o6n!ESp9nFx$cTj`}IWGjaB`~W3CV7wDO3u8-sqLsqUG~6XkZ<#XPo_ zQHnbtvjH+Y#(ytS_O4k{M9Bg4MU+C%Y=+Ek{EM=8&GKRO#VnuruO8(fQI2RU66Iog z-fN})ldRhumg4l!8R3v42dIy%wd=5e*?x61^yUElQ#r zv3`d{C-kE1#%lSAhUkfo=tbGPqGS=}Cd{Y37ir+cN3H!HO_W{lP2&7jlnYU3?7WXZ zIN$h->3R7J{pd4R{|BwkMxxaCO^LD_t9o?kiFW8k*=tcR_uB)Y@fRtih1Ir?^)ifg?fcrrIv}Sv+yd4cb&cR@cw_k zJga<_7p~*G8uR7Ky|FjdE6+W)-`F#c?IF*}6IT^>5+a>Z7~la zqq8J0Kl_8f{CykS#tSx6MxIWT{g=O=&)lZa|LO^gHZR#53*+=_HUy3&%0Bs0p4%3#^&e)jV-pGzHzWvREy`k=0W>K$brEKla`{{-6`^8z( zrMGy#!1vrye@Pl8*2?zIfcqfizPNyWpiFPAWA{VtA80>8lwJ47_{K)>4zf2kBk$*2 zl9gnG#k=oVNzNr&8wR^ru1W3eR2DNX^s%cHTcgy!V7|6{-*J~KclF$^U9H&XrdOX| zEa&899l~YN>l1l7Ik%v53JW~H(51Kb>F@l)9KFu9=$wlvd)IlFIeMLI;VG8$W1{R` z=Ur?j?hNQe(L<%NMj@T@)0Zb^c9!f`j&qg{^$3k z@fYpk*}+opGF7f_VF>n(Ps z$G^4zo4p(h+W2&ywk_>|uAqskiT9_gnb6{s-Cx$oz93 z_4cFuv|nwvZa)A0>{`vIAK>)Fx*^*?(+|}Cg?@n3C%;yfzwh?X^pqdH*Mt6JK00NG z#r!Sw1GN8}%JTdC?_>ODdM~%exyHRHd)NFg^aHg2Wo7w8`z&a;I*#8=Px;Y%Jr>yh zg}$Hm@3AVi^Ic)b;rPL$`|q~x2HP?45|{7cHrNw55gXFd*V%$il)Y^6q4mXkv<6y!G)! z+4VFX+rQA`pF0-*sW<+evd^i1m)`piJ^hAWr2nvY^_lQ!09=?18QIM>k>b zVBqqVt`}uD=KA72+%=Edd(T5n1E=3&?P%wBMA?m1J#XdF^R^znD0|o2`Z@laQ?9p> z9rU8?#)^OI9~(Ex!uuaWm~*FBXeyD|5#_h@0a;aY2R zPJTk(n=uYW#@{ukeSAW89j^U$BI9-AxQ|z;>lo+r7gjR54xIjgUr*%vF}y^ z?18!eVfDoff5)>P$=>m-N48sjAj%$?`!DpnZt)QW88NpUOJsz_^j1CosXzXyH~yWn zamA07P!=|A+M?6nAo{|hg;e#{~qdZ+C52!~%H86>iy7iF(SxSe}F!a*Y2uB#G> za4|h^zam1BP9%dwHuR$GwFrmKB3RH9+0cu!8!P^;e=Nf7w0CCwZPTu+5@oMNxLpr< zgo8x5o9;`r$kxR4+dh_`U z5l+{EL^iG`a{ZWI(OY|+_k5w8rgzF781#KcxLxx{xI?>rhCpV<5gSz^GbEEK_cAYrxJ;9F+Fd;BEpeQB!fgY^rGyw2#1bH28nFwMcHc+?$C?YKNjI2 z5$^C)iL%!s+>vWN!a*Y3k=GN6a4|ive-YshAM?lri4=@Ck@0uv5g(tB2y|pTk@0%h zJ|C};2p6w6pT7{{bR9@!<9Z_3kLeY?wbyyiM>)|uWe*Jc9uaO$*DCeKZT8Y>zIE#K z8J{DNxxpU0$Q+63d6|nCH-Nr~QPXFvPBzE64s#p-PT2!<|HJBw7_sVMk7cVK_Sm-i zGotK)x&K1H?1GOW$e3Ae^IT+%#q?G^{;5CysW<+evd^i1;U69S1nD>QqU^O8hks%m z`mqhJcgkLmap;I;kl2QQQTAGlTQ=%34ie**{WOsn7t{0hD`Fh!#4<>1Lodo+i*e|P zWsum0UX;BS|w*#m>V z&loo|(-Px4*00P3$lRE`JyG_qIa0*90rW+T>hPENHpjURa~uCo*#q-78&+S$h^6oK zShn=N9@~huyptpL@{TU1=l)CmW{VBmEuWEH&Ty_MQg-cf&i5{k>r;RHQ*Xt3#r~My z?%#Cu6MFg&y(oKE|CQx89kB>Ku?f8>dsmF=(t9jKKV~r!y;Jr%F|vqJou{q*7Na1s zY2u5Cve#l1I$|LtMxqyGH|Fiv<=@)h`n&VA$EJxdCd%Fwqq_7SBk@mc#J@<4#NHJn zi+6FH^ySO|lsd)Jvd#kdf2Cbs(cgY%8Qn4Z_a(03lR`k%IPTD&1fP5exh zJ+Pwp{OE~w=tZs~ znlnYr8$e>jqt#>SS}C>oDi>@02|-Z?j?b#hl$>Cu)do8^%1=u@6GbV_(GWfw})e zfA#l$%t6N7H5UIwVqQ#d)#IP~$G-#Ju%;5@oN&Jaoi1NUTFI%3h0kSKM#y zZ~YB{l+|N zmU&~|4Id42qJhkr8$XrEoQdgqnTwb=fWC-XH-x`NbU(~_{5!3#77Jo}?tfT)G3U4c zk;k^}f8?=_oe^RlJ0--tn4bGD_14dft?fp}+K%TEW$zkurQZBgPyADF?aXMW2LG1c z?%#Cu6MFg&y(oJv=Ak3DL1G1ZJ_$gxE4SZkYw9T_`c&xi|qbPe|Meq616YJ26#JqUjir%ho zagKb%I`mH21B1Tbn76~4WWMLTee0~uHfJDn<<9F8W$&6ZMa&yOU&O3^>#R;T=eZ7Z z9{*0+1M@Z;R$s)NgSUHZJ9xXtI`&G4dF-7K^J04Lztm6rm^)}>%pJ0R7KwS-YcY@O zQ-AzZZ|&7+?lszyX ze_eXdkDgeEUgUc5x)r@$-(nv5h;`_lvIhozkC<1UtmAKk6!D6Paph0!iZP`RJuX_2{epU8*`4?M1Ws#NkpR!0nyX&o<-txut*7jdq zy_LQ1v!o;0_*ray)69I_+kfWc-u^tT(hu?JTIsFrzu5XsZ};-+MCRL0c=_LULiWJc z@-Mc2|CL_O{a1Rqb?4WUKum8f|6=QJ`GJ>T=f(E?!t1f;7qZvef3fxN`AaY7d;ZeP zedZ-m_ImjjTmQ}vdO3BP<;25Y{u8znOI(fVt>b4A^f#rm{8q-kzg#+!>Fw$KoAy_L z_QVGH^666huDB?B`Q_76|DIc257FIsE)ji9&)cu?f9IE6578g|W+M8S-m0HzTo2Lj z*pP@mrnl;^Jm-3de#0jd(Z}>w{jpbE57D1onTS57x9V>>=6Z1%cOwaY%Zj?RU_x%^JUEi^Jk-wm|=NBVi%w6(SqU`!| zZd`F^{+s6X{jP`T&+JJ=AJbdP-!%K5c0ELY;ERdqV|uH8+qJHT=(oL|h(4yb>aTjp z^$`8l_a&l_>8<+Xt6UG!AOCqG`k3CT-~A!iL-c!&CZdn&t@=$ray>+U{hueIkLj)Y zH8ZY<=+`b!L?6>z^|TKwOUR0o_7i2VtvIiG$XA?@mFCs=C9>j-=@tLO>W3>o?L$2w z^``wq*=zM*Gvnol)PL>rMCu>YTkAi(ez@`zH>fA1-oy`4_FDa?UiA7y>d$xRMCu>Y zTkAi(ewgxGT|bB3Nxk{C-vq_yQ1t}Z(OqY`$p)8DZj^8`k6ROy@|h~ z?CteO=lu_<_qNv)>HnDC+W*7rz5fR-Kk=1%Lh4QY6=kp0f76e={*d}#|L2L+Kc=_V ze|Y_H<>&rPJt6hx{w>O0tN;F|z5bB;ANXP-^^fVT^&eh8T=}^_Q%^{}xqpkY*XqCf zLtcMK{r4PAr2a9zwf@8FhbuqNchnP7Z=U}|*=zMbeZSWqQvWl15~+VoZ>|6E`r*pY z^Bwhs)SKr&QTAH>kFWCjL+XG0=ZVxmrnlCAczxgH&)%3@U60uFqkX9!zW$um#lD*) z%KmEQ1nkr!=?u6`^LeN1oF zZ~Cz_4^-mJw$)tXNl-z zdaM4O-*-Jke`0$g`k3CTKfT`d5dE3+iRfc`t3LFb?YLZZi_II`ak=_2(XJEhf&bt1 zVceOY)&Jmr*PayJe@t)nKde5?8}mQ)i2GeSF1r7i-s*oieVq5!XR}A#Z}WtRf29|v zT(bG6^g;DLKj`O~=iTr2eWKSt%&YiYZSnm@=6`s7nD^#?ldYW@f7kz$=>B7RYx@tY z5A)Xi?>FE0Kkzfr{m1lH|HJA-+%f;VH@n|GpBLSKOmFo+tUkmI^M881`#od-O*@1* z66~QIt^SAAhqz~c-uZp^dt$rj^^fVT{s+)+OYfg8?tR$&eBH^n(5|-k>7wlAZ@GFu6<7`>JI4)lq#m%rs!>epQ7{vm$X+J2CT|CpZFzwp22I`buoz5Abf*8MK|K_dRKx7Xi%eG30h{PzAWy}bR85AlEIu0;ID^j80;Pq=?b{m%Qapu8ZZ~@}%pxJSlr%tA20Hw>RqU`eWDM^~bJ{>8<*k z-tYNtdcW&#zgLvK?*Co)dcJo>-Ten#fBym5>-xJUJ>OlEu6x)26=hHQ{!VPAU;DLn z{l%|$`bjO$GIC;zvyGhC5@qi?>s| z_FDNDS8t>7WyEFXW1t+y4|$Gx2)<>$nfNc+e1JYp2%f3fwP)S{gF1|BE2 zC_j|FHhvac&q*!H2`RVET0_}uTdb8-JYXMYp$5l$A_7y0e2!G=#J%3l7q>PVsIn@__r z74kuJtXM?ZYkF2H`kDJOc$jb4n*NhiwQ zORsXO+@kEY^7H();*>wXt=#F)Z=&qR*7(o!3;7^Ao_|EyYkKbg4_iYgXoAqqU<$2 z^N)NG9rIU|y{2dUk`JO|{EM=8>9Z3z)_+HAzSwCmxbi`JetIHN_UZM-_{;P*ZXQAJ zM9+yQQTCdilUn41=s2+@%3jlR(u{l%9VgC2*=u@E3Xu<@S!26GHJkXD_8}s;AjK5*^j63{#5{J}qzz2<*7 zz0dn8dw&_?fca(q;~&c2?tfUl&l~C)=8^ej{@@?V-tK>RJ@XzvAr6>d=0E^1+x z>Y2Cr3G>+eGJo+8Wv}@kR!`i)Pl!Y2m-vH!D0|KSuzKPKenK2Ezr+vxL)mNoht(7J z@Dt*o`6d40AIe_yKY+eCamPtN`*qTA;*Vb~<;0&Td-?07`H3~x*{_oZJ-=FtUX;E3 z_0m$$i9Y;5{Bq(?l)dI39eyBw(TlRz{BxoYKM=p1_!DKX`A3Hzh+p)g?Ct(7etio6 zPW*D>Pn5mgzw7iXvH0!%>#=3~bE1!WLi}>#Pn5mgzw4+cdiG}P)7{8?B2jUmKD0|I6C;IRM@ym%nQTAH>(cuT;7riKZ%|9pl@B{J7i9b>H zntycof%ru)%3kx&i9Y;5{Bq(?l)dI39eyBw(TlRz{BxoYKM=p1_!DKX`A3Hzh+p)g z>|Oqg6LajlO>Oc0x9P9={+lSfF&}@0o_#m+L3HfDiL%%9?7NW>V z^=8{5n&-rz`)8jGzwEz>_{ZMv-}l|f$Nn2S_TNO=YkKzG$OqA}|0c>_)3fhJK8TL} zH&OPQo_#m+L3HfDiL%%9?7NWjk-S-poxYl;6jhwVN z_xFji*G^mvpwGVXgu*H0;tapOdDc!E z@M|QHzil~Z|3rSx!F7{nQ}c* zc4I|v^|hyI^r4)lcgh}^>x(=5*$-QNe%JKIiQhK=A54_pnCpuOeRh-SZ#BJX3>d^KUwiwzjx){Zr@!ZVQTF!s_v>GH!S!PsT<@galwK=8{p0%O_9ZUbPn5l@ z|B49zZVL?DNg#KYQ&%O*Uc1Af<4y*-!=V>tuifF^z1PZY^9^!`IdxT{>|J-5Ipyb0 zhJWsC_!njGy2BN}X>;Zu{ky-A@BZHR!$iLO8`Jaglj&{TJ!<3c3(g7q-GEm=Vf_&) zduRUNr?xxR-0}PBw|)G=mhoHM@!tKo#~(Of{E6v#`3wEM7p(nl4&8gf#(~{APrXl+ zJ+Pwp{OGyUq8GVdylzEr*FW$1xYMF{${v{OvpcS}-^12Uzw4a%ZEOF}So=lF-gU># z^fqp`p?7k}KJ|9=PT9Nk*&W;b9x(l_PU7XMkC{GF_Rg&fy<;D}GQ5ShmU+&eubxPh-M&ANwOFpV-g*SR6MgURR+ReqU5fC13iQ3d zTT$xccPYa6DbV-+Zbhk&-=zrOr$FEPyA`Fr|L<0m`uJUn@O=vW_x^50sgK{K2;Zkb z-}}22r9OU_B7C0$eeds9l=}Exitv33^tJC+^nS+7My&nWSlfuTn6RFa>-$~3_f5+Z z_rGsi>cf83`hNCJOFjE$?9V~=>w52-min+?VtRY=-F;4{rv;vVdp|`qS$+NPsk^1_o}%pKT`<=Vau?0p z>kYk&PIo#7kKQwL>^A!)hw-Jhm{=A5e>1syv>9zin)NHktgRlirzTsiTwx&##{VDg znM;~x-Kx%U8eJDUyYb*I3jrwO|0+4R&y%yqs9a*d^2}Y|Mx&K@+Dja2K&dbBQq#-6 zraJeauMOh=S6NoBDo4tEs!r(vmB*r9am29-?T`LN3-^2pe`iW&IRS# zF*ldLLuxuH+6@$a3Vr|Qs&fB4$y(Fcy#aDx=zXrL)DQ4{kd@z`3;KLMDD(q79~All zo(~HB0M7@7et_qLLO;OsL80&G`Cz{BJHU#n@ISzcs?ZOxqAK(Qtf&h804u6OKfsEr z&=0VpD)a-as0#f6E2=`@&x$IJzlkD#K5S1QeZ9kIGW|eLUxmK^U5jS2DE|Q~{{r8E z#QJA?-|gt!>g3%?ZP#PkTrC^L|2m%L!o7~&CHuR<$;&$aM1QG0N6nXyXE)2&xvax; dbS;-*_zb(~VzBD5_iKI(%nu%K2#j&>oT1g_eQcU%oQ@>rZp94qkup`|l2q zNwp>HwMEg`q1_{+BE_;uv?4Egi8}Hk(%V^x}|CQ0=q!hcnUrhUr zJlgNzHyB?A|Ms+a<4!XlBRNbTdAHH`w747&`RKbD&K+fZ8Ei1W(&FL#^q60n+eQz+ z9WA~jf0Tazg`3DfjrqF?Zwk4eHCmj^j6dZg_r0VYq=)$*G+NyIt-5{%mp^)jt5{xH zz@K$l-APIu&)+9S3Y#=*Nt)J2If{lx#`~n$CepUbYNf*3XdF)-l&t;ot!;CfBhwnK z%Y80_C*V!K2|Qs*AA8Pn70OX`oY2v9RrQ%hi+7yJ%y6c{8^`0g#tH@JKcMev@miA# zjnBq${VG-#e0&D)bIjA?^{biunH>_J6D_w=V6}AdVt#Ix=?$=~h zMSjC(oWzqHOPDsSwisKpvtdt5_WOJ%!g%tVB|4^g^Ym4A&ECqq6~uRCc6=^_6Lqs5#1+-DeHvfpgnMvFHa ze{BMra10d%mbmv(okojCW0~=n)8|F~6r2H6)G55nEs>UwjTR@H`B<;5>Q{bN-)QmH ztr;KR6Mx<_Z>g+)*R1+Ri=&Rx=Y5O!G1kqPzfY9KaS-RF(c)mh$GrW3`MaPj;oDRH z4C8UMcyuA-mwbM#?H8Dz8_M_>tmCKn+0o*S8_j$;!}Kxs*Xes&T!x$btyk1<`%!(P z#apj5_uIFtUwK)5qs7~|t6y0>@1M%!R)7;#_Z`+JT8mRV(+j)QdM%y?<+ag)x5ltQ z{Cc~m!A<%_Pv-V(c zRC)SGn47{Tk@Z*<()~Rd-xHpXd*Gp=k-Z1-l6ZPWpPpo6h8E4hb4_b(a*AC1dwK>k z--D&+Vg0tAhh*S9%=j@^tcq#@JEu^KSV^PBE7i;jrhJ@*7wIeMwHMVVE#A!UexUtG z`fIRVjPzZ5;Qd85>`$chx89kM?`gckMxc0)5zzq<>`E)8gU$tUG*z za1SCIf3W^MEnW}ar&xFNmGs+->XR04=665PekA=hxLX+MyY}GwZ`h$eNpIsNPx`Js z==YpapQOL#ktcoE9`yU}sZY}HAM~W}+JpYiUiC@(1Lr*HyY`^JcS?Pd{=QjH`mR0b zAH1tRNq@B8lfG*Y`V&?4N%}{oJ?Xplw4b>T*7o6?+=X)%Ed7puisC+Kw0N{Dvwu>) z$k879N_vym)h8{!#LwKPYTu&YY2*h>C-E*{ds;j?oyni_Mc#RdzOr-@<9~s^(&Ekh zqaUhI(wltWlfG+D=C6XT`2P#L!QeiG_Br$~^QLiN^fi3jmbvL9{jjsy5BH`w-1m67 Yr?D4)@!qp|Zwd8VdOyPVw^)?_0C~PB;Q#;t literal 0 HcmV?d00001 From c80e335cd6f39330de655919373206e2afe89899 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 7 Jan 2019 02:08:59 +0100 Subject: [PATCH 187/335] WIP: global nets integration in cluster builder. --- src/db/db/dbHierNetworkProcessor.cc | 387 +++++++++++++----- src/db/db/dbHierNetworkProcessor.h | 21 + src/db/db/dbLayoutToNetlist.cc | 6 +- src/db/db/dbLayoutToNetlist.h | 5 + src/db/db/dbNetlist.cc | 14 - src/db/db/dbNetlist.h | 26 -- src/db/db/dbNetlistDeviceClasses.cc | 16 - src/db/db/dbNetlistDeviceClasses.h | 11 +- src/db/db/dbNetlistDeviceExtractorClasses.cc | 36 +- src/db/db/dbNetlistDeviceExtractorClasses.h | 34 +- src/db/db/dbNetlistExtractor.cc | 78 +--- src/db/db/gsiDeclDbHierNetworkProcessor.cc | 5 +- src/db/db/gsiDeclDbNetlist.cc | 58 --- .../unit_tests/dbHierNetworkProcessorTests.cc | 34 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 364 ++++++++++++++-- src/db/unit_tests/dbNetlistExtractorTests.cc | 8 +- src/db/unit_tests/dbNetlistTests.cc | 50 --- testdata/algo/hc_test_au16.gds | Bin 0 -> 5570 bytes testdata/algo/hc_test_au16b.gds | Bin 0 -> 4874 bytes testdata/algo/hc_test_l16.gds | Bin 0 -> 1162 bytes testdata/ruby/dbNetlist.rb | 7 - 21 files changed, 749 insertions(+), 411 deletions(-) create mode 100644 testdata/algo/hc_test_au16.gds create mode 100644 testdata/algo/hc_test_au16b.gds create mode 100644 testdata/algo/hc_test_l16.gds diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index dccedd970..587f83790 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -104,17 +104,8 @@ Connectivity::end_global_connections (unsigned int l) const size_t Connectivity::connect_global (unsigned int l, const std::string &gn) { - for (std::vector::const_iterator i = m_global_net_names.begin (); i != m_global_net_names.end (); ++i) { - if (*i == gn) { - size_t id = i - m_global_net_names.begin (); - m_global_connections [l].insert (id); - return id; - } - } - - size_t id = m_global_net_names.size (); + size_t id = global_net_id (gn); m_global_connections [l].insert (id); - m_global_net_names.push_back (gn); return id; } @@ -131,6 +122,21 @@ Connectivity::global_net_name (size_t id) const return m_global_net_names [id]; } +size_t +Connectivity::global_net_id (const std::string &gn) +{ + for (std::vector::const_iterator i = m_global_net_names.begin (); i != m_global_net_names.end (); ++i) { + if (*i == gn) { + size_t id = i - m_global_net_names.begin (); + return id; + } + } + + size_t id = m_global_net_names.size (); + m_global_net_names.push_back (gn); + return id; +} + Connectivity::layer_iterator Connectivity::begin_layers () const { @@ -225,6 +231,7 @@ local_cluster::clear () m_size = 0; m_bbox = box_type (); m_attrs.clear (); + m_global_nets.clear (); } template @@ -262,6 +269,7 @@ local_cluster::join_with (const local_cluster &other) } m_attrs.insert (other.m_attrs.begin (), other.m_attrs.end ()); + m_global_nets.insert (other.m_global_nets.begin (), other.m_global_nets.end ()); m_size += other.size (); m_needs_update = true; @@ -1450,95 +1458,7 @@ private: */ ClusterInstance make_path (id_type id, const std::vector &path) const { - std::vector::const_iterator p = path.end (); - tl_assert (p != path.begin ()); - - while (true) { - - --p; - - ClusterInstance ci (id, *p); - if (p == path.begin ()) { - - // if we're attaching to a child which is root yet, we need to promote the - // cluster to the parent in all places - connected_clusters &child_cc = mp_tree->clusters_per_cell (p->inst_ptr.cell_index ()); - if (child_cc.is_root (id)) { - - const db::Cell &child_cell = mp_layout->cell (p->inst_ptr.cell_index ()); - for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { - - connected_clusters &parent_cc = mp_tree->clusters_per_cell (pi->parent_cell_index ()); - for (db::CellInstArray::iterator pii = pi->child_inst ().begin (); ! pii.at_end (); ++pii) { - - ClusterInstance ci2 (id, db::InstElement (pi->child_inst (), pii)); - if (mp_cell->cell_index () != pi->parent_cell_index () || ci != ci2) { - - id_type id_dummy = parent_cc.insert_dummy (); - parent_cc.add_connection (id_dummy, ci2); - - } - - } - - } - - child_cc.reset_root (id); - - } - - return ci; - - } - - db::cell_index_type pci = p [-1].inst_ptr.cell_index (); - connected_clusters &target_cc = mp_tree->clusters_per_cell (pci); - id_type parent_cluster = target_cc.find_cluster_with_connection (ci); - - if (parent_cluster > 0) { - - // taken parent - id = parent_cluster; - - } else { - - id_type id_new = 0; - - // if we're attaching to a child which is root yet, we need to promote the - // cluster to the parent in all places - connected_clusters &child_cc = mp_tree->clusters_per_cell (p->inst_ptr.cell_index ()); - if (child_cc.is_root (id)) { - - const db::Cell &child_cell = mp_layout->cell (p->inst_ptr.cell_index ()); - for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { - - connected_clusters &parent_cc = mp_tree->clusters_per_cell (pi->parent_cell_index ()); - for (db::CellInstArray::iterator pii = pi->child_inst ().begin (); ! pii.at_end (); ++pii) { - - id_type id_dummy = parent_cc.insert_dummy (); - ClusterInstance ci2 (id, db::InstElement (pi->child_inst (), pii)); - parent_cc.add_connection (id_dummy, ci2); - - if (pci == pi->parent_cell_index () && ci == ci2) { - id_new = id_dummy; - } - - } - - } - - child_cc.reset_root (id); - - } - - // no parent -> create vertical connector - id = id_new; - tl_assert (id != 0); - - } - - } - + return mp_tree->make_path (*mp_layout, *mp_cell, id, path); } }; @@ -1565,6 +1485,120 @@ private: } +template +ClusterInstance +hier_clusters::make_path (const db::Layout &layout, const db::Cell &cell, size_t id, const std::vector &path) +{ + std::vector::const_iterator p = path.end (); + tl_assert (p != path.begin ()); + + while (true) { + + --p; + + ClusterInstance ci (id, *p); + if (p == path.begin ()) { + + // if we're attaching to a child which is root yet, we need to promote the + // cluster to the parent in all places + connected_clusters &child_cc = clusters_per_cell (p->inst_ptr.cell_index ()); + if (child_cc.is_root (id)) { + + const db::Cell &child_cell = layout.cell (p->inst_ptr.cell_index ()); + for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + + connected_clusters &parent_cc = clusters_per_cell (pi->parent_cell_index ()); + for (db::CellInstArray::iterator pii = pi->child_inst ().begin (); ! pii.at_end (); ++pii) { + + ClusterInstance ci2 (id, db::InstElement (pi->child_inst (), pii)); + if (cell.cell_index () != pi->parent_cell_index () || ci != ci2) { + + size_t id_dummy; + + const typename db::local_cluster::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets (); + if (gn.empty ()) { + id_dummy = parent_cc.insert_dummy (); + } else { + local_cluster *lc = parent_cc.insert (); + lc->set_global_nets (gn); + id_dummy = lc->id (); + } + + parent_cc.add_connection (id_dummy, ci2); + + } + + } + + } + + child_cc.reset_root (id); + + } + + return ci; + + } + + db::cell_index_type pci = p [-1].inst_ptr.cell_index (); + connected_clusters &target_cc = clusters_per_cell (pci); + size_t parent_cluster = target_cc.find_cluster_with_connection (ci); + + if (parent_cluster > 0) { + + // taken parent + id = parent_cluster; + + } else { + + size_t id_new = 0; + + // if we're attaching to a child which is root yet, we need to promote the + // cluster to the parent in all places + connected_clusters &child_cc = clusters_per_cell (p->inst_ptr.cell_index ()); + if (child_cc.is_root (id)) { + + const db::Cell &child_cell = layout.cell (p->inst_ptr.cell_index ()); + for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + + connected_clusters &parent_cc = clusters_per_cell (pi->parent_cell_index ()); + for (db::CellInstArray::iterator pii = pi->child_inst ().begin (); ! pii.at_end (); ++pii) { + + size_t id_dummy; + + const typename db::local_cluster::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets (); + if (gn.empty ()) { + id_dummy = parent_cc.insert_dummy (); + } else { + local_cluster *lc = parent_cc.insert (); + lc->set_global_nets (gn); + id_dummy = lc->id (); + } + + ClusterInstance ci2 (id, db::InstElement (pi->child_inst (), pii)); + parent_cc.add_connection (id_dummy, ci2); + + if (pci == pi->parent_cell_index () && ci == ci2) { + id_new = id_dummy; + } + + } + + } + + child_cc.reset_root (id); + + } + + // no parent -> create vertical connector + id = id_new; + tl_assert (id != 0); + + } + + } +} + template void hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) @@ -1648,6 +1682,83 @@ hier_clusters::build_hier_connections_for_cells (cell_clusters_box_converter< } } +namespace { + +class GlobalNetClusterMaker +{ +public: + typedef std::pair, std::set > entry_type; + typedef std::list entry_list; + typedef entry_list::const_iterator entry_iterator; + + void + add (const std::set &global_nets, size_t cluster_id) + { + add (global_nets, ClusterInstance (cluster_id, db::InstElement ())); + } + + void + add (const std::set &global_nets, const ClusterInstance &inst) + { + if (global_nets.empty ()) { + return; + } + + std::set::const_iterator g0 = global_nets.begin (); + + std::map::iterator k = m_global_net_to_entries.find (*g0); + if (k == m_global_net_to_entries.end ()) { + + m_entries.push_back (entry_type ()); + m_entries.back ().first.insert (*g0); + + k = m_global_net_to_entries.insert (std::make_pair (*g0, --m_entries.end ())).first; + + } + + k->second->second.insert (inst); + + for (std::set::const_iterator g = ++g0; g != global_nets.end (); ++g) { + + std::map::iterator j = m_global_net_to_entries.find (*g); + if (j == m_global_net_to_entries.end ()) { + + k->second->first.insert (*g); + k->second->second.insert (inst); + + m_global_net_to_entries.insert (std::make_pair (*g, k->second)); + + } else if (k->second != j->second) { + + // joining required + k->second->first.insert (j->second->first.begin (), j->second->first.end ()); + k->second->second.insert (j->second->second.begin (), j->second->second.end ()); + + m_entries.erase (j->second); + j->second = k->second; + + } + + } + } + + entry_iterator begin () const + { + return m_entries.begin (); + } + + entry_iterator end () const + { + return m_entries.end (); + } + +private: + entry_list m_entries; + std::map m_global_net_to_entries; +}; + +} + template void hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn) @@ -1662,7 +1773,7 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // NOTE: this is a receiver for both the child-to-child and // local to child interactions. - hc_receiver rec (layout, cell, local, *this, cbc, conn); + std::auto_ptr > rec (new hc_receiver (layout, cell, local, *this, cbc, conn)); cell_inst_clusters_box_converter cibc (cbc); // The box scanner needs pointers so we have to first store the instances @@ -1694,7 +1805,7 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c bs.insert (inst.operator-> (), 0); } - bs.process (rec, 1 /*touching*/, cibc); + bs.process (*rec, 1 /*touching*/, cibc); } // handle local to instance connections @@ -1729,11 +1840,79 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c bs2.insert2 (inst.operator-> (), 0); } - bs2.process (rec, 1 /*touching*/, local_cluster_box_convert (), cibc); + bs2.process (*rec, 1 /*touching*/, local_cluster_box_convert (), cibc); } - // finally join local clusters which got connected by child clusters - rec.join_superclusters (); + // join local clusters which got connected by child clusters + rec->join_superclusters (); + rec.reset (0); + + // finally connect global nets + { + static std::string desc = tl::to_string (tr ("Global net treatment")); + tl::SelfTimer timer (tl::verbosity () >= 51, desc); + + GlobalNetClusterMaker global_net_clusters; + + // insert the global nets from the subcircuits which need connection + + for (std::vector::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) { + + const db::connected_clusters &cc = m_per_cell_clusters [inst->cell_index ()]; + for (typename db::connected_clusters::const_iterator cl = cc.begin (); cl != cc.end (); ++cl) { + + if (! cl->get_global_nets ().empty () && cc.is_root (cl->id ())) { + for (db::Instance::cell_inst_array_type::iterator i = inst->begin (); !i.at_end (); ++i) { + global_net_clusters.add (cl->get_global_nets (), db::ClusterInstance (cl->id (), db::InstElement (*inst, i))); + } + } + + } + } + + // insert the global nets from here + + for (typename db::connected_clusters::const_iterator cl = local.begin (); cl != local.end (); ++cl) { + if (! cl->get_global_nets ().empty ()) { + global_net_clusters.add (cl->get_global_nets (), db::ClusterInstance (cl->id (), db::InstElement ())); + } + } + + // now global_net_clusters knows what clusters need to be made for the global nets + + for (GlobalNetClusterMaker::entry_iterator ge = global_net_clusters.begin (); ge != global_net_clusters.end (); ++ge) { + + db::local_cluster *gc = local.insert (); + gc->set_global_nets (ge->first); + + for (std::set::const_iterator ci = ge->second.begin (); ci != ge->second.end (); ++ci) { + + if (ci->inst ().array_inst.at_end ()) { + + local.join_cluster_with (gc->id (), ci->id ()); + local.remove_cluster (ci->id ()); + + } else { + + std::vector p; + p.push_back (ci->inst ()); + ClusterInstance k = make_path (layout, cell, ci->id (), p); + + size_t other_id = local.find_cluster_with_connection (k); + if (other_id) { + local.join_cluster_with (gc->id (), other_id); + local.remove_cluster (other_id); + } else { + local.add_connection (gc->id (), k); + } + + } + + } + + } + + } } template diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index e1bfe3746..b33801e9f 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -99,6 +99,11 @@ public: */ const std::string &global_net_name (size_t id) const; + /** + * @brief Gets the global net ID for the given name + */ + size_t global_net_id (const std::string &gn); + /** * @brief Begin iterator for the layers involved */ @@ -306,6 +311,14 @@ public: return m_global_nets.end (); } + /** + * @brief Gets the global nets set + */ + const global_nets &get_global_nets () const + { + return m_global_nets; + } + /** * @brief Sets the global nets */ @@ -767,6 +780,14 @@ public: */ void clear (); + /** + * @brief Makes a valid path to a child cluster + * + * Cluster connections can only cross one level of hierarchy. This method + * creates necessary dummy entries for the given path. + */ + ClusterInstance make_path (const db::Layout &layout, const db::Cell &cell, size_t id, const std::vector &path); + private: void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); void build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn); diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 2c4a09424..f6cceff9c 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -170,6 +170,11 @@ const std::string &LayoutToNetlist::global_net_name (size_t id) const return m_conn.global_net_name (id); } +size_t LayoutToNetlist::global_net_id (const std::string &name) +{ + return m_conn.global_net_id (name); +} + void LayoutToNetlist::extract_netlist () { if (m_netlist_extracted) { @@ -275,7 +280,6 @@ static void deliver_shapes_of_net_nonrecursive (const db::NetlistExtractor &nete tl_assert (circuit != 0); db::cell_index_type ci = circuit->cell_index (); - const db::local_cluster &lc = netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); for (db::local_cluster::shape_iterator s = lc.begin (layer_id); !s.at_end (); ++s) { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index c43cac8fb..ad6c8f033 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -176,6 +176,11 @@ public: */ const std::string &global_net_name (size_t id) const; + /** + * @brief Gets the global net ID for a given name name + */ + size_t global_net_id (const std::string &name); + /** * @brief Runs the netlist extraction * See the class description for more details. diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index eeb88a042..914f6a882 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -113,22 +113,8 @@ const Net *Device::net_for_terminal (size_t terminal_id) const return 0; } -void Device::connect_terminal_global (size_t terminal_id, size_t global_net_id) -{ - connect_terminal (terminal_id, 0); - m_global_connections.push_back (std::make_pair (terminal_id, global_net_id)); -} - void Device::connect_terminal (size_t terminal_id, Net *net) { - for (size_t i = 0; i < m_global_connections.size (); ) { - if (m_global_connections [i].first == terminal_id) { - m_global_connections.erase (m_global_connections.begin () + i); - } else { - ++i; - } - } - if (net_for_terminal (terminal_id) == net) { return; } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 3af81c79c..1c6acb2ad 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -884,31 +884,6 @@ public: return m_name; } - /** - * @brief Gets the global connections iterator (begin) - * Global connections are terminals attached to a global net. - * This iterator delivers a pair of terminal ID (first) - * and global net ID (second). - * See Connectivity for the definition of the global net ID. - */ - global_connections_iterator begin_global_connections () const - { - return m_global_connections.begin (); - } - - /** - * @brief Gets the global connections iterator (end) - */ - global_connections_iterator end_global_connections () const - { - return m_global_connections.end (); - } - - /** - * @brief Connects the given terminal to the given global net - */ - void connect_terminal_global (size_t terminal_id, size_t global_net_id); - /** * @brief Gets the net attached to a specific terminal * Returns 0 if no net is attached. @@ -966,7 +941,6 @@ private: std::vector m_parameters; size_t m_id; Circuit *mp_circuit; - global_connections m_global_connections; /** * @brief Sets the terminal reference for a specific terminal diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index 11f125a71..7b61d6a09 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -250,27 +250,11 @@ bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const // ------------------------------------------------------------------------------------ // DeviceClassMOS4Transistor implementation -DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_L = 0; -DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_W = 1; -DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_AS = 2; -DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_AD = 3; - -DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_S = 0; -DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_G = 1; -DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_D = 2; DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_B = 3; DeviceClassMOS4Transistor::DeviceClassMOS4Transistor () { - add_terminal_definition (db::DeviceTerminalDefinition ("S", "Source")); - add_terminal_definition (db::DeviceTerminalDefinition ("G", "Gate")); - add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain")); add_terminal_definition (db::DeviceTerminalDefinition ("B", "Bulk")); - - add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0)); } bool DeviceClassMOS4Transistor::combine_devices (Device *a, Device *b) const diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index dccc580d9..95408a2dd 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -181,19 +181,11 @@ public: * terminal for the bulk. */ class DB_PUBLIC DeviceClassMOS4Transistor - : public db::DeviceClass + : public DeviceClassMOS3Transistor { public: DeviceClassMOS4Transistor (); - static size_t param_id_L; - static size_t param_id_W; - static size_t param_id_AS; - static size_t param_id_AD; - - static size_t terminal_id_S; - static size_t terminal_id_G; - static size_t terminal_id_D; static size_t terminal_id_B; virtual db::DeviceClass *clone () const @@ -202,7 +194,6 @@ public: } virtual bool combine_devices (Device *a, Device *b) const; - virtual bool supports_parallel_combination () const { return true; } }; } diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index 0eb21f6cc..0d3dafbd9 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -24,12 +24,12 @@ #include "dbNetlistDeviceExtractorClasses.h" #include "dbNetlistDeviceClasses.h" -// --------------------------------------------------------------------------------- -// NetlistDeviceExtractorMOS3Transistor implementation - namespace db { +// --------------------------------------------------------------------------------- +// NetlistDeviceExtractorMOS3Transistor implementation + NetlistDeviceExtractorMOS3Transistor::NetlistDeviceExtractorMOS3Transistor (const std::string &name) : db::NetlistDeviceExtractor (name) { @@ -47,7 +47,7 @@ void NetlistDeviceExtractorMOS3Transistor::setup () db::Connectivity NetlistDeviceExtractorMOS3Transistor::get_connectivity (const db::Layout & /*layout*/, const std::vector &layers) const { - tl_assert (layers.size () == 3); + tl_assert (layers.size () >= 3); unsigned int diff = layers [0]; unsigned int gate = layers [1]; @@ -117,6 +117,9 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector & /*layer_geometry*/, db::Device *device) +{ + unsigned int well_geometry_index = 3; + define_terminal (device, db::DeviceClassMOS4Transistor::terminal_id_B, well_geometry_index, rgate); +} + } diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.h b/src/db/db/dbNetlistDeviceExtractorClasses.h index 32e2e538a..2a2b3cb35 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.h +++ b/src/db/db/dbNetlistDeviceExtractorClasses.h @@ -35,7 +35,7 @@ namespace db * The device is defined by two basic input layers: the diffusion area * (source and drain) and the gate area. It requires a third layer * (poly) to put the gate terminals on. The separation between poly - * and allows separating the device recognition layer (gate) from the + * and gate allows separating the device recognition layer (gate) from the * conductive layer. * * The device class produced by this extractor is DeviceClassMOS3Transistor. @@ -56,13 +56,43 @@ public: protected: /** - * @brief A cappback when the device is produced + * @brief A callback when the device is produced * This callback is provided as a debugging port */ virtual void device_out (const db::Device * /*device*/, const db::Region & /*diff*/, const db::Region & /*gate*/) { // .. no specific implementation .. } + + /** + * @brief Allow derived classes to modify the device + */ + virtual void modify_device (const db::Polygon & /*rgate*/, const std::vector & /*layer_geometry*/, db::Device * /*device*/) + { + // .. no specific implementation .. + } + +}; + +/** + * @brief A device extractor for a four-terminal MOS transistor + * + * This class is like the MOS3Transistor extractor, but requires a forth + * input layer (Well). This layer will be used to output the bulk terminal. + * + * The device class produced by this extractor is DeviceClassMOS4Transistor. + * The extractor extracts the four parameters of this class: L, W, AS and AD. + */ +class DB_PUBLIC NetlistDeviceExtractorMOS4Transistor + : public NetlistDeviceExtractorMOS3Transistor +{ +public: + NetlistDeviceExtractorMOS4Transistor (const std::string &name); + + virtual void setup (); + +private: + virtual void modify_device (const db::Polygon &rgate, const std::vector &layer_geometry, db::Device *device); }; } diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 7fe733bbf..38ae498df 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -65,8 +65,6 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } std::map > pins_per_cluster_per_cell; - std::map > global_nets_per_cell; - for (db::Layout::bottom_up_const_iterator cid = layout.begin_bottom_up (); cid != layout.end_bottom_up (); ++cid) { const connected_clusters_type &clusters = m_net_clusters.clusters_per_cell (*cid); @@ -89,7 +87,6 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect circuit = k->second; } - std::map &global_nets = global_nets_per_cell [*cid]; std::map &c2p = pins_per_cluster_per_cell [*cid]; std::map subcircuits; @@ -100,12 +97,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect net->set_cluster_id (*c); circuit->add_net (net); - const db::local_cluster &cluster = clusters.cluster_by_id (*c); - - // collect global net assignments from clusters - for (std::set::const_iterator g = cluster.begin_global_nets (); g != cluster.end_global_nets (); ++g) { - tl_assert (global_nets.find (*g) == global_nets.end ()); - global_nets.insert (std::make_pair (*g, net)); + const db::local_cluster::global_nets &gn = clusters.cluster_by_id (*c).get_global_nets (); + for (db::local_cluster::global_nets::const_iterator g = gn.begin (); g != gn.end (); ++g) { assign_net_name (conn.global_net_name (*g), net); } @@ -134,73 +127,6 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } - // make global net connections for devices - for (db::Circuit::device_iterator d = circuit->begin_devices (); d != circuit->end_devices (); ++d) { - - for (db::Device::global_connections_iterator g = d->begin_global_connections (); g != d->end_global_connections (); ++g) { - - db::Net *&net = global_nets [g->second]; - if (! net) { - net = new db::Net (conn.global_net_name (g->second)); - circuit->add_net (net); - } - - net->add_terminal (db::NetTerminalRef (d.operator-> (), g->first)); - - } - - } - - // if any of the subcircuits has global nets which this circuit doesn't have, propagate them - - std::set seen; - std::set global_nets_of_subcircuits; - for (db::Circuit::subcircuit_iterator sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) { - - db::Circuit *subcircuit = sc->circuit_ref (); - if (seen.find (subcircuit) == seen.end ()) { - - seen.insert (subcircuit); - - const std::map &sc_gn = global_nets_per_cell [subcircuit->cell_index ()]; - for (std::map::const_iterator g = sc_gn.begin (); g != sc_gn.end (); ++g) { - global_nets_of_subcircuits.insert (g->first); - } - - } - - } - - for (std::set::const_iterator g = global_nets_of_subcircuits.begin (); g != global_nets_of_subcircuits.end (); ++g) { - - } - - // make the global net connections into subcircuits - if necessary by creating pins into the subcircuit - for (db::Circuit::subcircuit_iterator sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) { - - db::Circuit *subcircuit = sc->circuit_ref (); - - const std::map &sc_gn = global_nets_per_cell [subcircuit->cell_index ()]; - for (std::map::const_iterator g = global_nets.begin (); g != global_nets.end (); ++g) { - - std::map::const_iterator gg = sc_gn.find (g->first); - if (gg != sc_gn.end ()) { - - size_t pin_id = 0; - if (gg->second->pin_count () > 0) { - pin_id = gg->second->begin_pins ()->pin_id (); - } else { - pin_id = subcircuit->add_pin (conn.global_net_name (gg->first)).id (); - subcircuit->connect_pin (pin_id, gg->second); - } - g->second->add_subcircuit_pin (db::NetSubcircuitPinRef (sc.operator-> (), pin_id)); - - } - - } - - } - } } diff --git a/src/db/db/gsiDeclDbHierNetworkProcessor.cc b/src/db/db/gsiDeclDbHierNetworkProcessor.cc index 3537bf7e1..d9c794a0a 100644 --- a/src/db/db/gsiDeclDbHierNetworkProcessor.cc +++ b/src/db/db/gsiDeclDbHierNetworkProcessor.cc @@ -37,8 +37,11 @@ Class decl_dbConnectivity ("db", "Connectivity", "@brief Connects the given layer to the global net given by name.\n" "Returns the ID of the global net." ) + - gsi::method ("global", &db::Connectivity::global_net_name, gsi::arg ("global_net_id"), + gsi::method ("global_net_name", &db::Connectivity::global_net_name, gsi::arg ("global_net_id"), "@brief Gets the name for a given global net ID.\n" + ) + + gsi::method ("global_net_id", &db::Connectivity::global_net_id, gsi::arg ("global_net_name"), + "@brief Gets the ID for a given global net name.\n" ), "@brief This class specifies connections between different layers." "Connections are build using \\connect. There are basically two flavours of connections: intra-layer and inter-layer.\n" diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 716f909fd..6f87c25ac 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -61,44 +61,6 @@ static void device_disconnect_terminal_by_name (db::Device *device, const std::s device_connect_terminal_by_name (device, terminal_name, 0); } -static tl::Variant device_terminal_for_global_net (const db::Device *device, size_t global_net) -{ - for (db::Device::global_connections_iterator g = device->begin_global_connections (); g != device->end_global_connections (); ++g) { - if (g->second == global_net) { - return tl::Variant (g->first); - } - } - return tl::Variant (); -} - -static tl::Variant device_global_net_for_terminal (const db::Device *device, size_t terminal_id) -{ - for (db::Device::global_connections_iterator g = device->begin_global_connections (); g != device->end_global_connections (); ++g) { - if (g->first == terminal_id) { - return tl::Variant (g->second); - } - } - return tl::Variant (); -} - -static tl::Variant device_global_net_for_terminal_name (const db::Device *device, const std::string &terminal_name) -{ - if (! device->device_class ()) { - throw tl::Exception (tl::to_string (tr ("Device does not have a device class"))); - } - size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name); - return device_global_net_for_terminal (device, terminal_id); -} - -static void device_connect_terminal_global_by_name (db::Device *device, const std::string &terminal_name, size_t global_net) -{ - if (! device->device_class ()) { - throw tl::Exception (tl::to_string (tr ("Device does not have a device class"))); - } - size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name); - device->connect_terminal_global (terminal_id, global_net); -} - Class decl_dbDevice ("db", "Device", gsi::method ("device_class", &db::Device::device_class, "@brief Gets the device class the device belongs to.\n" @@ -140,26 +102,6 @@ Class decl_dbDevice ("db", "Device", "@brief Disconnects the given terminal from any net.\n" "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised." ) + - gsi::method ("connect_terminal_global", &db::Device::connect_terminal_global, gsi::arg ("terminal_id"), gsi::arg ("global_net_id"), - "@brief Connects the given terminal to the given global net.\n" - "The global net ID is taken from \\Connectivity (connect_global, etc.).\n" - "If the terminal was already connected to another net, it will be disconnected from there." - ) + - gsi::method_ext ("connect_terminal_global", &device_connect_terminal_global_by_name, gsi::arg ("terminal_name"), gsi::arg ("global_net_id"), - "@brief Connects the given terminal to the given global net.\n" - "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised." - ) + - gsi::method_ext ("terminal_on_global_net", &device_terminal_for_global_net, gsi::arg ("global_net_id"), - "@brief Gets the terminal ID for the given global net or nil if no terminal is not that global net.\n" - "The global net ID is managed by the \\Connectivity object." - ) + - gsi::method_ext ("global_net_on_terminal", &device_global_net_for_terminal, gsi::arg ("terminal_id"), - "@brief Gets the global net ID for the given terminal ID or nil if the terminal is not connected to a global net.\n" - ) + - gsi::method_ext ("global_net_on_terminal", &device_global_net_for_terminal_name, gsi::arg ("terminal_name"), - "@brief Gets the global net ID for the given terminal name or nil if the terminal is not connected to a global net.\n" - "If the name is not a valid terminal name, an exception is raised." - ) + gsi::method ("parameter", (double (db::Device::*) (size_t) const) &db::Device::parameter_value, gsi::arg ("param_id"), "@brief Gets the parameter value for the given parameter ID." ) + diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 8890780a6..8ef049e6f 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -877,17 +877,12 @@ static void copy_cluster_shapes (const std::string *&attrs, db::Shapes &out, db: static void run_hc_test (tl::TestBase *_this, const std::string &file, const std::string &au_file) { db::Layout ly; - unsigned int l0 = 0, l1 = 0, l2 = 0, l3 = 0; + unsigned int l1 = 0, l2 = 0, l3 = 0, l4 = 0; { db::LayerProperties p; db::LayerMap lmap; - p.layer = 0; - p.datatype = 0; - lmap.map (db::LDPair (p.layer, p.datatype), l0 = ly.insert_layer ()); - ly.set_properties (l0, p); - p.layer = 1; p.datatype = 0; lmap.map (db::LDPair (p.layer, p.datatype), l1 = ly.insert_layer ()); @@ -903,6 +898,11 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std lmap.map (db::LDPair (p.layer, p.datatype), l3 = ly.insert_layer ()); ly.set_properties (l3, p); + p.layer = 4; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l4 = ly.insert_layer ()); + ly.set_properties (l4, p); + db::LoadLayoutOptions options; options.get_options ().layer_map = lmap; options.get_options ().create_other_layers = false; @@ -919,6 +919,7 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std normalize_layer (ly, strings, l1); normalize_layer (ly, strings, l2); normalize_layer (ly, strings, l3); + normalize_layer (ly, strings, l4); // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 db::Connectivity conn; @@ -927,6 +928,9 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std conn.connect (l3, l3); conn.connect (l1, l2); conn.connect (l1, l3); + conn.connect (l1, l4); + + conn.connect_global (l4, "BULK"); db::hier_clusters hc; hc.build (ly, ly.cell (*ly.begin_top_down ()), db::ShapeIterator::Polygons, conn); @@ -989,7 +993,7 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::string &file, const std::string &au_file) { db::Layout ly; - unsigned int l1 = 0, l2 = 0, l3 = 0; + unsigned int l1 = 0, l2 = 0, l3 = 0, l4 = 0; { db::LayerProperties p; @@ -1010,6 +1014,11 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str lmap.map (db::LDPair (p.layer, p.datatype), l3 = ly.insert_layer ()); ly.set_properties (l3, p); + p.layer = 4; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l4 = ly.insert_layer ()); + ly.set_properties (l4, p); + db::LoadLayoutOptions options; options.get_options ().layer_map = lmap; options.get_options ().create_other_layers = false; @@ -1026,6 +1035,7 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str normalize_layer (ly, strings, l1); normalize_layer (ly, strings, l2); normalize_layer (ly, strings, l3); + normalize_layer (ly, strings, l4); // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 db::Connectivity conn; @@ -1034,6 +1044,9 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str conn.connect (l3, l3); conn.connect (l1, l2); conn.connect (l1, l3); + conn.connect (l1, l4); + + conn.connect_global (l4, "BULK"); db::hier_clusters hc; hc.build (ly, ly.cell (*ly.begin_top_down ()), db::ShapeIterator::Polygons, conn); @@ -1042,6 +1055,7 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str lm[l1] = ly.insert_layer (db::LayerProperties (101, 0)); lm[l2] = ly.insert_layer (db::LayerProperties (102, 0)); lm[l3] = ly.insert_layer (db::LayerProperties (103, 0)); + lm[l4] = ly.insert_layer (db::LayerProperties (104, 0)); hc.return_to_hierarchy (ly, lm); CHECKPOINT(); @@ -1138,3 +1152,9 @@ TEST(115_HierClusters) run_hc_test_with_backannotation (_this, "hc_test_l15.gds", "hc_test_au15b.gds"); } +TEST(116_HierClusters) +{ + run_hc_test (_this, "hc_test_l16.gds", "hc_test_au16.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l16.gds", "hc_test_au16b.gds"); +} + diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index e631f9b26..565dc49fd 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -49,11 +49,39 @@ static std::string device_name (const db::Device &device) } } -class MOSFETExtractor +static void mos2layout (const db::Layout *layout, db::cell_index_type cell_index, db::Layout *debug_out, const db::Device *device, unsigned int ldiff, const db::Region &diff, unsigned int lgate, const db::Region &gate) +{ + std::string cn = layout->cell_name (cell_index); + std::pair target_cp = debug_out->cell_by_name (cn.c_str ()); + tl_assert (target_cp.first); + + db::cell_index_type dci = debug_out->add_cell ((device->device_class ()->name () + "_" + device->circuit ()->name () + "_" + device_name (*device)).c_str ()); + debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ())); + + db::Cell &device_cell = debug_out->cell (dci); + for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) { + device_cell.shapes (ldiff).insert (*p); + } + for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) { + device_cell.shapes (lgate).insert (*p); + } + + std::string ps; + const std::vector &pd = device->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (! ps.empty ()) { + ps += ","; + } + ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ())); + } + device_cell.shapes (ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ()))); +} + +class MOSFET3Extractor : public db::NetlistDeviceExtractorMOS3Transistor { public: - MOSFETExtractor (const std::string &name, db::Layout *debug_out) + MOSFET3Extractor (const std::string &name, db::Layout *debug_out) : db::NetlistDeviceExtractorMOS3Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) { if (mp_debug_out) { @@ -68,34 +96,34 @@ private: void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) { - if (! mp_debug_out) { - return; + if (mp_debug_out) { + mos2layout (layout (), cell_index (), mp_debug_out, device, m_ldiff, diff, m_lgate, gate); } + } +}; - std::string cn = layout ()->cell_name (cell_index ()); - std::pair target_cp = mp_debug_out->cell_by_name (cn.c_str ()); - tl_assert (target_cp.first); - - db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->circuit ()->name () + "_" + device_name (*device)).c_str ()); - mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ())); - - db::Cell &device_cell = mp_debug_out->cell (dci); - for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) { - device_cell.shapes (m_ldiff).insert (*p); - } - for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) { - device_cell.shapes (m_lgate).insert (*p); +class MOSFET4Extractor + : public db::NetlistDeviceExtractorMOS4Transistor +{ +public: + MOSFET4Extractor (const std::string &name, db::Layout *debug_out) + : db::NetlistDeviceExtractorMOS4Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) + { + if (mp_debug_out) { + m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); + m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); } + } - std::string ps; - const std::vector &pd = device->device_class ()->parameter_definitions (); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (! ps.empty ()) { - ps += ","; - } - ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ())); +private: + db::Layout *mp_debug_out; + unsigned int m_ldiff, m_lgate; + + void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) + { + if (mp_debug_out) { + mos2layout (layout (), cell_index (), mp_debug_out, device, m_ldiff, diff, m_lgate, gate); } - device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ()))); } }; @@ -261,8 +289,8 @@ TEST(1_Basic) // NOTE: the device extractor will add more debug layers for the transistors: // 20/0 -> Diffusion // 21/0 -> Gate - MOSFETExtractor pmos_ex ("PMOS", &ly); - MOSFETExtractor nmos_ex ("NMOS", &ly); + MOSFET3Extractor pmos_ex ("PMOS", &ly); + MOSFET3Extractor nmos_ex ("NMOS", &ly); // device extraction @@ -617,8 +645,8 @@ TEST(2_Probing) // NOTE: the device extractor will add more debug layers for the transistors: // 20/0 -> Diffusion // 21/0 -> Gate - MOSFETExtractor pmos_ex ("PMOS", &ly); - MOSFETExtractor nmos_ex ("NMOS", &ly); + MOSFET3Extractor pmos_ex ("PMOS", &ly); + MOSFET3Extractor nmos_ex ("NMOS", &ly); // device extraction @@ -871,8 +899,8 @@ TEST(3_GlobalNetConnections) // NOTE: the device extractor will add more debug layers for the transistors: // 20/0 -> Diffusion // 21/0 -> Gate - MOSFETExtractor pmos_ex ("PMOS", &ly); - MOSFETExtractor nmos_ex ("NMOS", &ly); + MOSFET3Extractor pmos_ex ("PMOS", &ly); + MOSFET3Extractor nmos_ex ("NMOS", &ly); // device extraction @@ -1054,3 +1082,277 @@ TEST(3_GlobalNetConnections) EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I8"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); } + +TEST(4_GlobalNetDeviceExtraction) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int pplus = define_layer (ly, lmap, 10); + unsigned int nplus = define_layer (ly, lmap, 11); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l3.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rbulk (l2n.make_layer (ly.insert_layer ())); + std::auto_ptr rnwell (l2n.make_layer (nwell)); + std::auto_ptr ractive (l2n.make_layer (active)); + std::auto_ptr rpplus (l2n.make_layer (pplus)); + std::auto_ptr rnplus (l2n.make_layer (nplus)); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell & *rpplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell & *rnplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion + unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie + unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpsd.insert_into (&ly, tc.cell_index (), lptie); + rnsd.insert_into (&ly, tc.cell_index (), lntie); + + // NOTE: the device extractor will add more debug layers for the transistors: + // 20/0 -> Diffusion + // 21/0 -> Gate + MOSFET4Extractor pmos_ex ("PMOS", &ly); + MOSFET4Extractor nmos_ex ("NMOS", &ly); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rdiff_cont, rptie); + l2n.connect (*rdiff_cont, rntie); + l2n.connect (*rnwell, rntie); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + l2n.connect_global (*rbulk, "BULK"); + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 201/0 -> Well + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + // 212/0 -> N tie + // 213/0 -> P tie + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2PAIR $1 ($1=VSS,$2=VSS,$3=FB,$4=VDD,$5=VSS,$6=$I7,$7=OSC,$8=VDD)\n" + " XINV2PAIR $2 ($1=VSS,$2=VSS,$3=$I22,$4=VDD,$5=VSS,$6=FB,$7=$I13,$8=VDD)\n" + " XINV2PAIR $3 ($1=VSS,$2=VSS,$3=$I23,$4=VDD,$5=VSS,$6=$I13,$7=$I5,$8=VDD)\n" + " XINV2PAIR $4 ($1=VSS,$2=VSS,$3=$I24,$4=VDD,$5=VSS,$6=$I5,$7=$I6,$8=VDD)\n" + " XINV2PAIR $5 ($1=VSS,$2=VSS,$3=$I25,$4=VDD,$5=VSS,$6=$I6,$7=$I7,$8=VDD)\n" + "Circuit INV2PAIR ($1=$I10,$2=$I9,$3=$I8,$4=$I6,$5=$I5,$6=$I3,$7=$I2,$8=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I4,$3=$I8,BULK=$I10,OUT=$I2,VSS=$I5,VDD=$I6)\n" + " XINV2 $2 ($1=$I1,IN=$I3,$3=$I7,BULK=$I9,OUT=$I4,VSS=$I5,VDD=$I6)\n" + "Circuit INV2 ($1=$1,IN=IN,$3=$3,BULK=BULK,OUT=OUT,VSS=VSS,VDD=VDD):\n" + " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " XTRANS $1 ($1=$3,$2=VSS,$3=IN)\n" + " XTRANS $2 ($1=$3,$2=VDD,$3=IN)\n" + " XTRANS $3 ($1=VDD,$2=OUT,$3=$3)\n" + " XTRANS $4 ($1=VSS,$2=OUT,$3=$3)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au3_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + + // do some probing before purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I22"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,VSS=VSS):\n" + " XINV2PAIR $1 ($1=VSS,$2=VSS,$3=FB,$4=VDD,$5=VSS,$6=$I7,$7=OSC,$8=VDD)\n" + " XINV2PAIR $2 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=FB,$7=$I13,$8=VDD)\n" + " XINV2PAIR $3 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I13,$7=$I5,$8=VDD)\n" + " XINV2PAIR $4 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I5,$7=$I6,$8=VDD)\n" + " XINV2PAIR $5 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I6,$7=$I7,$8=VDD)\n" + "Circuit INV2PAIR ($1=$I10,$2=$I9,$3=$I8,$4=$I6,$5=$I5,$6=$I3,$7=$I2,$8=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I4,$3=$I8,BULK=$I10,OUT=$I2,VSS=$I5,VDD=$I6)\n" + " XINV2 $2 ($1=$I1,IN=$I3,$3=(null),BULK=$I9,OUT=$I4,VSS=$I5,VDD=$I6)\n" + "Circuit INV2 ($1=(null),IN=IN,$3=$3,BULK=(null),OUT=OUT,VSS=VSS,VDD=VDD):\n" + " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + ); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + // the transistor which supplies this probe target has been optimized away by "purge". + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I8"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); +} diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 6625e2c30..3dd6e3630 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -63,11 +63,11 @@ static std::string device_name (const db::Device &device) namespace { -class MOSFETExtractor +class MOSFET3Extractor : public db::NetlistDeviceExtractorMOS3Transistor { public: - MOSFETExtractor (const std::string &name, db::Layout *debug_out) + MOSFET3Extractor (const std::string &name, db::Layout *debug_out) : db::NetlistDeviceExtractorMOS3Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) { if (mp_debug_out) { @@ -239,8 +239,8 @@ TEST(1_DeviceAndNetExtraction) // NOTE: the device extractor will add more debug layers for the transistors: // 20/0 -> Diffusion // 21/0 -> Gate - MOSFETExtractor pmos_ex ("PMOS", &ly); - MOSFETExtractor nmos_ex ("NMOS", &ly); + MOSFET3Extractor pmos_ex ("PMOS", &ly); + MOSFET3Extractor nmos_ex ("NMOS", &ly); db::NetlistDeviceExtractor::input_layers dl; diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 08ede9d34..07682ab06 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -1024,53 +1024,3 @@ TEST(12_NetlistTopology) EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2"); EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1"); } - -TEST(13_DeviceGlobalNets) -{ - db::DeviceTerminalDefinition pd; - pd.set_name ("name"); - pd.set_description ("nothing yet"); - - db::DeviceTerminalDefinition pd2; - pd2.set_name ("name2"); - pd2.set_description ("now it has something"); - - db::DeviceClass dc; - dc.set_name ("devname"); - dc.set_description ("devdesc"); - dc.add_terminal_definition (pd); - dc.add_terminal_definition (pd2); - - db::Device d (&dc); - db::Net n; - - d.connect_terminal_global (0, 17); - - db::Device::global_connections_iterator g; - - g = d.begin_global_connections (); - EXPECT_EQ (g != d.end_global_connections (), true); - EXPECT_EQ (g->first, size_t (0)); - EXPECT_EQ (g->second, size_t (17)); - - ++g; - EXPECT_EQ (g == d.end_global_connections (), true); - - d.connect_terminal (0, &n); - g = d.begin_global_connections (); - EXPECT_EQ (g == d.end_global_connections (), true); - EXPECT_EQ (d.net_for_terminal (0) == &n, true); - - d.connect_terminal_global (0, 17); - EXPECT_EQ (d.net_for_terminal (0) == 0, true); - - g = d.begin_global_connections (); - EXPECT_EQ (g != d.end_global_connections (), true); - EXPECT_EQ (g->first, size_t (0)); - EXPECT_EQ (g->second, size_t (17)); - - d.connect_terminal (0, 0); - g = d.begin_global_connections (); - EXPECT_EQ (g == d.end_global_connections (), true); - EXPECT_EQ (d.net_for_terminal (0) == 0, true); -} diff --git a/testdata/algo/hc_test_au16.gds b/testdata/algo/hc_test_au16.gds new file mode 100644 index 0000000000000000000000000000000000000000..77d3c83edd3076e9aa32cbdd3e8595f89be73df8 GIT binary patch literal 5570 zcmeI0zfYZ27{|Zdd%4`=gc;TzlV?Tetc6Mapr?0*Wm;dKX)BbnmL&~rLofEfaoZH+CG=05VfBYYd96#%v_+%N_b2ZTP_1T*7xiEjmy26#1 z_AHEZ{5?)IrRijL{+aR4ZHzki*Q&_1+i>pBpNyu9^~M8DCBE+z0cIaBTrrxSF#G-a z5WjOz2ekI0;!f5&rRhm->W<&It$ZG@VSw zv!tI_rM)OLvr1o7y_PF^uEzqPk8+bV;pO{sz77bEQdj$^L_Zrjt=#bV8|L{{nlN^~Ns( zO(&zg+P?ACCYH8RR=A6Zg&oz08AA zc}?F(dG(xO9Xtla$|7*OJz0|XgaygE21O&o^@m|iiaCmbmeN}&=jekq(}EZN11Yn{`YLn4RdUH77#V|V(KB9PHvlbiBY!XYP~*K zO=5+pzWRi|M%79BKFVw88V~6SKzw134G$IPkrE>St1Vr)j!$!%WG$!)1v zymFZ#+J2j6EZM2j^DfP0pU-85bLm{D$;C=G7p)b)=AwF{Z8NaUwM^fKIJwtn_RAVC z)xYiE^3r!3O(%mFZ#VV4G)|{TGM;^=^;f@%rjt=#`#SwRM9uFI0yV$%eU#UM5uW-> zfLJL2hrbRqos9C*8NgFAISUk*J{wIZqr7w@()x&sO=pVHbTZ0ow#Q=GPKNs82~@AQU!l*%89*FkFCM!cXgV3ZcGxEr&!q=Gu1~M|xQwQg zQC_-7eK_IoQ)BmmBM%u@O4G?GuOsa|iyqU1{Jb;SgP+lKGRjMFon%joeO=sh_!vzm zgVzpwv^;bN7klP-weJWtoeW+(ti8_e?y^6_Pu*Z#8-UStGRmv{uD_p&=lX$zjLq0T z$$j6ykMcV31MiF6Tg7+p`8*m;C!@Udy{b=9cjH6Tfu@sDUOEq7uX3Lz-{hn9Gn!6D zd1*fHE%^DJuK@2eMx*Iu@Yy$-gjyx&~!4&YqutRt^ChxVl{ie*Ey{panDumH;aL$llAJW#_NrF{^m3w=6>}1 xv(a=i%1is5?#rU?`1-riX!;KK=$g3RxugHR1H{rTzyBCbC%47LzqJYl{0p!*62kxh literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au16b.gds b/testdata/algo/hc_test_au16b.gds new file mode 100644 index 0000000000000000000000000000000000000000..97fea40b2c4496069c240bacb1f733060625a975 GIT binary patch literal 4874 zcmd6qyKfv-6vjXAW4tkIvnHFDSt3IWB484~LLQhrYy*yLDHd45G6~g$qk|z<|Z&`)1~3?nGRL0%RWci{-NyzC1bh({~%EN0yE+d^0_X zMmg+@qG-4mr3w(W7WnL_t-z;u*NV>V+_wv8>h>?wMU>j{_kK6;+@DqF+&ob11j==z z>0;Rb=&Ey@4d=w2S?9L5Gn&5H2>X9srhm>k@zENv=W<5VH|KKxLiYZQ^&vAcldv>S z{~f+)O4CXI{XM^PTcghXzAiF%Th9IVqtSFR^qpzbTT;pYdz{_Kx}1oj?HE?oecWB*OPY_pIrd@_%24} zCcW?bKl9(8(W5e;|MaoD%GWZQP6qwGrHVPp2y>)95f%UIA7?cEiT=rr|02g!zD3pk6|N9Q z)5&=Je=sxpAJ2?FL>)~mO0`iQnpSoA7tfV__v6p;6I+s5)R)}pT#0MpziV!;?EQj$ z#yp9~7^|LSG@bPQxn3Wpw>k%i^1hcoLo}TX`rlcPXAf38*^utr)RMAc}TF&IrJWB*fXRf(d>OAN)IzG z+SC|x*HSLFTyE8Cww9^?^?jr1r2lR?zrL$gH1@EMl@Il^XgV46@AmjFjI*AzfH(%u zqs5G-lfn3pYycNmIgiPYm^;lwqv>SOuXRIvjo3TSEvz%6>15Ea`Os5M=EaHYK*ttf zG@T6kPp<%P-2ue&HQ+4!!Du=e^bg(u1~+(-L#6y>IvMou_V}uo{=_|(fuqYAO(%o# zzq-J?Gk{q50XQ>|(R9-H=hl|JU7S12yV$-B9OC1QrjtSc{t=$;SUa&+V*S6$XgV46 z@AmlW`*$aj{vW#w9KOf-m(p}H7=QhCvWJVx{XxzlqxSane$c=Fd!8MxF@L1`UE>=~ zCxia;Yrwft&KYvJhws8TjHZ)8|L`{FUWFd=!XPlpIvPzUgZ|wf|K&!~f1>sP?dL}I zUwYq$A~xJE1d8`h}VE7au13ji@=Gv!hY_PeAxIY3n3pV`T1 zIvMn9&(ppkYQH_fIvPzUgZ|wf|IJ(aMGlCSo5@~iG@bP0zvBOn&>CN5{zT0i^}f+` z@;~$I+;V9=IlsKWlI(*<)5+lY2UdaOBgy)$T6FL-nob7&T5I24O4eMRJI*a8=MT|z M@?Y`uY+WkhFOL|(sQ>@~ literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l16.gds b/testdata/algo/hc_test_l16.gds new file mode 100644 index 0000000000000000000000000000000000000000..f4bb6149eb0634fd6fa9143b50184b27d05b6e8d GIT binary patch literal 1162 zcma))F;Cl26opT0=fyyRQV69&9vGNVN<}gt5JDn!01>GjG9V@fhW>zz9WpRbsbdGE zGO%^&*db$wiiv@dfgutM9XcQ-p=ld&d#~+_Z3zn^J$=@3&hvTaJ{VZm4!n$&yFvyA z(^$dt_%mF~)VFFt$A?qfISOjOG?#+>HdC`sOu34hwq67gNUkky>$QYGxoQL zg#8O(WG|xX-B!YHMd#thfKHwLKlq>_RcFS}AM+FSHi`cJ5^{GvqCel2steQn zK+fg&b3%ZpkFkbQb)WI?+fja^+g0gaD7pQvL#po2Ce`2jD)Zv}O!r9okElA&HO0^K zzU01(0snmi@*a^=b*Asn;n7=KfiudtbVBZHzG!nFZJ@9j_>*s5zU8kaC+kGb0HYrz zO~COc5MQb@pPH_z%687V7{2 literal 0 HcmV?d00001 diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 86e1954c7..812304085 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -259,13 +259,6 @@ class DBNetlist_TestClass < TestBase d2.connect_terminal(0, net) assert_equal(net.terminal_count, 1) - d2.connect_terminal_global(0, 1) - assert_equal(net.terminal_count, 0) - - assert_equal(d2.terminal_on_global_net(1), 0) - assert_equal(d2.terminal_on_global_net(17).inspect, "nil") - assert_equal(d2.global_net_on_terminal(0), 1) - assert_equal(d2.global_net_on_terminal(1).inspect, "nil") end From 315bcdd0165e921b660ec972e0a49f2ec81644e2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 7 Jan 2019 23:33:57 +0100 Subject: [PATCH 188/335] WIP: bugfixed netlist extractor with global nets. --- src/db/db/dbHierNetworkProcessor.cc | 1 + src/db/unit_tests/dbLayoutToNetlistTests.cc | 100 +++++++++--------- .../algo/device_extract_au3_with_rec_nets.gds | Bin 49126 -> 49146 bytes .../algo/device_extract_au4_with_rec_nets.gds | Bin 0 -> 51962 bytes testdata/algo/hc_test_au16.gds | Bin 5570 -> 4506 bytes testdata/algo/hc_test_au16b.gds | Bin 4874 -> 3978 bytes 6 files changed, 52 insertions(+), 49 deletions(-) create mode 100644 testdata/algo/device_extract_au4_with_rec_nets.gds diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 587f83790..91c5aaab0 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -106,6 +106,7 @@ Connectivity::connect_global (unsigned int l, const std::string &gn) { size_t id = global_net_id (gn); m_global_connections [l].insert (id); + m_all_layers.insert (l); return id; } diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 565dc49fd..85aa1c411 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -1001,15 +1001,15 @@ TEST(3_GlobalNetConnections) // compare netlist as string EXPECT_EQ (l2n.netlist ()->to_string (), "Circuit RINGO ():\n" - " XINV2PAIR $1 ($1=VSS,$2=VSS,$3=FB,$4=VDD,$5=VSS,$6=$I7,$7=OSC,$8=VDD)\n" - " XINV2PAIR $2 ($1=VSS,$2=VSS,$3=$I22,$4=VDD,$5=VSS,$6=FB,$7=$I13,$8=VDD)\n" - " XINV2PAIR $3 ($1=VSS,$2=VSS,$3=$I23,$4=VDD,$5=VSS,$6=$I13,$7=$I5,$8=VDD)\n" - " XINV2PAIR $4 ($1=VSS,$2=VSS,$3=$I24,$4=VDD,$5=VSS,$6=$I5,$7=$I6,$8=VDD)\n" - " XINV2PAIR $5 ($1=VSS,$2=VSS,$3=$I25,$4=VDD,$5=VSS,$6=$I6,$7=$I7,$8=VDD)\n" - "Circuit INV2PAIR ($1=$I10,$2=$I9,$3=$I8,$4=$I6,$5=$I5,$6=$I3,$7=$I2,$8=$I1):\n" - " XINV2 $1 ($1=$I1,IN=$I4,$3=$I8,BULK=$I10,OUT=$I2,VSS=$I5,VDD=$I6)\n" - " XINV2 $2 ($1=$I1,IN=$I3,$3=$I7,BULK=$I9,OUT=$I4,VSS=$I5,VDD=$I6)\n" - "Circuit INV2 ($1=$1,IN=IN,$3=$3,BULK=BULK,OUT=OUT,VSS=VSS,VDD=VDD):\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=$I23,$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=$I24,$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=$I25,$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" @@ -1038,7 +1038,7 @@ TEST(3_GlobalNetConnections) EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); - EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:BULK,VSS"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I22"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); @@ -1052,16 +1052,16 @@ TEST(3_GlobalNetConnections) // compare netlist as string EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,VSS=VSS):\n" - " XINV2PAIR $1 ($1=VSS,$2=VSS,$3=FB,$4=VDD,$5=VSS,$6=$I7,$7=OSC,$8=VDD)\n" - " XINV2PAIR $2 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=FB,$7=$I13,$8=VDD)\n" - " XINV2PAIR $3 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I13,$7=$I5,$8=VDD)\n" - " XINV2PAIR $4 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I5,$7=$I6,$8=VDD)\n" - " XINV2PAIR $5 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I6,$7=$I7,$8=VDD)\n" - "Circuit INV2PAIR ($1=$I10,$2=$I9,$3=$I8,$4=$I6,$5=$I5,$6=$I3,$7=$I2,$8=$I1):\n" - " XINV2 $1 ($1=$I1,IN=$I4,$3=$I8,BULK=$I10,OUT=$I2,VSS=$I5,VDD=$I6)\n" - " XINV2 $2 ($1=$I1,IN=$I3,$3=(null),BULK=$I9,OUT=$I4,VSS=$I5,VDD=$I6)\n" - "Circuit INV2 ($1=(null),IN=IN,$3=$3,BULK=(null),OUT=OUT,VSS=VSS,VDD=VDD):\n" + "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS'):\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=(null),IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=(null)):\n" " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" @@ -1245,6 +1245,7 @@ TEST(4_GlobalNetDeviceExtraction) dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (214, 0)); dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); @@ -1262,6 +1263,7 @@ TEST(4_GlobalNetDeviceExtraction) dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); + dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (314, 0)); dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); @@ -1275,19 +1277,19 @@ TEST(4_GlobalNetDeviceExtraction) // compare netlist as string EXPECT_EQ (l2n.netlist ()->to_string (), "Circuit RINGO ():\n" - " XINV2PAIR $1 ($1=VSS,$2=VSS,$3=FB,$4=VDD,$5=VSS,$6=$I7,$7=OSC,$8=VDD)\n" - " XINV2PAIR $2 ($1=VSS,$2=VSS,$3=$I22,$4=VDD,$5=VSS,$6=FB,$7=$I13,$8=VDD)\n" - " XINV2PAIR $3 ($1=VSS,$2=VSS,$3=$I23,$4=VDD,$5=VSS,$6=$I13,$7=$I5,$8=VDD)\n" - " XINV2PAIR $4 ($1=VSS,$2=VSS,$3=$I24,$4=VDD,$5=VSS,$6=$I5,$7=$I6,$8=VDD)\n" - " XINV2PAIR $5 ($1=VSS,$2=VSS,$3=$I25,$4=VDD,$5=VSS,$6=$I6,$7=$I7,$8=VDD)\n" - "Circuit INV2PAIR ($1=$I10,$2=$I9,$3=$I8,$4=$I6,$5=$I5,$6=$I3,$7=$I2,$8=$I1):\n" - " XINV2 $1 ($1=$I1,IN=$I4,$3=$I8,BULK=$I10,OUT=$I2,VSS=$I5,VDD=$I6)\n" - " XINV2 $2 ($1=$I1,IN=$I3,$3=$I7,BULK=$I9,OUT=$I4,VSS=$I5,VDD=$I6)\n" - "Circuit INV2 ($1=$1,IN=IN,$3=$3,BULK=BULK,OUT=OUT,VSS=VSS,VDD=VDD):\n" - " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=$I23,$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=$I24,$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=$I25,$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" + " DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " XTRANS $1 ($1=$3,$2=VSS,$3=IN)\n" " XTRANS $2 ($1=$3,$2=VDD,$3=IN)\n" " XTRANS $3 ($1=VDD,$2=OUT,$3=$3)\n" @@ -1300,7 +1302,7 @@ TEST(4_GlobalNetDeviceExtraction) std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); au = tl::combine_path (au, "algo"); - au = tl::combine_path (au, "device_extract_au3_with_rec_nets.gds"); + au = tl::combine_path (au, "device_extract_au4_with_rec_nets.gds"); db::compare_layouts (_this, ly, au); @@ -1312,7 +1314,7 @@ TEST(4_GlobalNetDeviceExtraction) EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); - EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:BULK,VSS"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I22"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); @@ -1326,20 +1328,20 @@ TEST(4_GlobalNetDeviceExtraction) // compare netlist as string EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,VSS=VSS):\n" - " XINV2PAIR $1 ($1=VSS,$2=VSS,$3=FB,$4=VDD,$5=VSS,$6=$I7,$7=OSC,$8=VDD)\n" - " XINV2PAIR $2 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=FB,$7=$I13,$8=VDD)\n" - " XINV2PAIR $3 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I13,$7=$I5,$8=VDD)\n" - " XINV2PAIR $4 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I5,$7=$I6,$8=VDD)\n" - " XINV2PAIR $5 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I6,$7=$I7,$8=VDD)\n" - "Circuit INV2PAIR ($1=$I10,$2=$I9,$3=$I8,$4=$I6,$5=$I5,$6=$I3,$7=$I2,$8=$I1):\n" - " XINV2 $1 ($1=$I1,IN=$I4,$3=$I8,BULK=$I10,OUT=$I2,VSS=$I5,VDD=$I6)\n" - " XINV2 $2 ($1=$I1,IN=$I3,$3=(null),BULK=$I9,OUT=$I4,VSS=$I5,VDD=$I6)\n" - "Circuit INV2 ($1=(null),IN=IN,$3=$3,BULK=(null),OUT=OUT,VSS=VSS,VDD=VDD):\n" - " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS'):\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" + " DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" ); // do some probing after purging diff --git a/testdata/algo/device_extract_au3_with_rec_nets.gds b/testdata/algo/device_extract_au3_with_rec_nets.gds index 9ce7a8522f323e714516ce1c2ea0cbb35cef025e..e9fbf4d083321a7b09513ba46450c2406f682dee 100644 GIT binary patch delta 1221 zcmaF%pXt|sCM5s_Qwp24D1ZT45|!L$n1$qs>0ZWqhhfNpSp`pc=9er z?Bc7~=VMd9c@@)KMywK1EI1^%*m|++h~c=)gw-e??pUlkChy_F>e$U|cvZ1#*!+YK zhlGp(4v7#!91=Z3mDpV?A$pkwt2h2hKE^KbMXr}|@_PlI$(t2oCx6(+Hu=IfmdPCo zGMfby(|NEO)unqLyHPntI3)bcaYz{0T*Pjah~q=-5*%(gB>s5ekoe&LkVOki+GLPm zV+-oC~|Nc#Y3 z9w5yVYdraXT<>I`M3>0{2{w~?5`kg~4wKJ-a6C{wknV|h*xZx&T>@(uPOFmFz$$TK zQ9m2fb-3O#K zHUZ^s_Qwp2K%O9j1_L`Xd!mx6FgD?+SggXQ?qU<3yo(X5 z_~sbqxs2E}Z02FbA+d+87psoRtC;3vH7SPUE)!M>AMRM}Chg(D?$|ZFs#rB_e!_=C zLPh|GM2H{`i5{U!?5>p%z088u8~-F9W0&|M*ULEht%CC82SQwv9TZq6_bSA07Eny* z!D>vG?s@FSz- zKFHJ0-9J7oIC$~{bFsxjp&XMh#Tid_jq{y+Db`_f5D-s_bC@gy#FKz*p;+U|b7idz3 X%4GiSDU*G-^H1KSAijD2_Gk_Om9oZE diff --git a/testdata/algo/device_extract_au4_with_rec_nets.gds b/testdata/algo/device_extract_au4_with_rec_nets.gds new file mode 100644 index 0000000000000000000000000000000000000000..7f0110515f3ba96ed0deab151e7b093c384df61d GIT binary patch literal 51962 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_au16.gds b/testdata/algo/hc_test_au16.gds index 77d3c83edd3076e9aa32cbdd3e8595f89be73df8..96153688c45d5532132afe6fdbd6327fb4902fe6 100644 GIT binary patch delta 150 zcmX@4Jxf`MfsKKQDS|gCsJ0qLR8GhTz7isZ5xno0uD!F-12| hVmr-W5dc7n85aNm delta 146 zcmbQGd`Me~fsKKQDS|ylSuU}IonieQjoWMY5Jz{tSPAkHAeAc@SLsH854A-FMWDifyYCT31%Owr9! zY_l0Lg(n|lzqDC`vw?B31UKX689WY*lOy;JY@WdnVhA=&mJnLQ#?HXN!omOm&Bq(X delta 167 zcmeB@?^07@U}IonieQjoWMY5Jz{tSPz`&ryV1mq^sH854A-FMWDifyYCT31%Owr9! yY_l0Lg(n|lzqD9_gK6^&&IZQKGq?^gZoa|o0A%qTU|f8ImvQn8zBiLy_}u{C-yfg= From 294f1701b5d37334a8c119e1246fd441a202bbf4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 7 Jan 2019 23:57:52 +0100 Subject: [PATCH 189/335] Added a test for joining of layers through multiple global net assignment. --- .../unit_tests/dbHierNetworkProcessorTests.cc | 46 +++++++++++++++++- testdata/algo/hc_test_au17.gds | Bin 0 -> 3174 bytes testdata/algo/hc_test_au17b.gds | Bin 0 -> 2872 bytes testdata/algo/hc_test_l17.gds | Bin 0 -> 1272 bytes 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 testdata/algo/hc_test_au17.gds create mode 100644 testdata/algo/hc_test_au17b.gds create mode 100644 testdata/algo/hc_test_l17.gds diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 8ef049e6f..06798f741 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -877,7 +877,7 @@ static void copy_cluster_shapes (const std::string *&attrs, db::Shapes &out, db: static void run_hc_test (tl::TestBase *_this, const std::string &file, const std::string &au_file) { db::Layout ly; - unsigned int l1 = 0, l2 = 0, l3 = 0, l4 = 0; + unsigned int l1 = 0, l2 = 0, l3 = 0, l4 = 0, l5 = 0, l6 = 0; { db::LayerProperties p; @@ -903,6 +903,16 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std lmap.map (db::LDPair (p.layer, p.datatype), l4 = ly.insert_layer ()); ly.set_properties (l4, p); + p.layer = 5; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l5 = ly.insert_layer ()); + ly.set_properties (l5, p); + + p.layer = 6; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l6 = ly.insert_layer ()); + ly.set_properties (l6, p); + db::LoadLayoutOptions options; options.get_options ().layer_map = lmap; options.get_options ().create_other_layers = false; @@ -920,6 +930,8 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std normalize_layer (ly, strings, l2); normalize_layer (ly, strings, l3); normalize_layer (ly, strings, l4); + normalize_layer (ly, strings, l5); + normalize_layer (ly, strings, l6); // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 db::Connectivity conn; @@ -929,8 +941,13 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std conn.connect (l1, l2); conn.connect (l1, l3); conn.connect (l1, l4); + conn.connect (l1, l5); + conn.connect (l1, l6); conn.connect_global (l4, "BULK"); + conn.connect_global (l5, "BULK2"); + conn.connect_global (l6, "BULK"); + conn.connect_global (l6, "BULK2"); db::hier_clusters hc; hc.build (ly, ly.cell (*ly.begin_top_down ()), db::ShapeIterator::Polygons, conn); @@ -993,7 +1010,7 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::string &file, const std::string &au_file) { db::Layout ly; - unsigned int l1 = 0, l2 = 0, l3 = 0, l4 = 0; + unsigned int l1 = 0, l2 = 0, l3 = 0, l4 = 0, l5 = 0, l6 = 0; { db::LayerProperties p; @@ -1019,6 +1036,16 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str lmap.map (db::LDPair (p.layer, p.datatype), l4 = ly.insert_layer ()); ly.set_properties (l4, p); + p.layer = 5; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l5 = ly.insert_layer ()); + ly.set_properties (l5, p); + + p.layer = 6; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l6 = ly.insert_layer ()); + ly.set_properties (l6, p); + db::LoadLayoutOptions options; options.get_options ().layer_map = lmap; options.get_options ().create_other_layers = false; @@ -1036,6 +1063,8 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str normalize_layer (ly, strings, l2); normalize_layer (ly, strings, l3); normalize_layer (ly, strings, l4); + normalize_layer (ly, strings, l5); + normalize_layer (ly, strings, l6); // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 db::Connectivity conn; @@ -1045,8 +1074,13 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str conn.connect (l1, l2); conn.connect (l1, l3); conn.connect (l1, l4); + conn.connect (l1, l5); + conn.connect (l1, l6); conn.connect_global (l4, "BULK"); + conn.connect_global (l5, "BULK2"); + conn.connect_global (l6, "BULK"); + conn.connect_global (l6, "BULK2"); db::hier_clusters hc; hc.build (ly, ly.cell (*ly.begin_top_down ()), db::ShapeIterator::Polygons, conn); @@ -1056,6 +1090,8 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str lm[l2] = ly.insert_layer (db::LayerProperties (102, 0)); lm[l3] = ly.insert_layer (db::LayerProperties (103, 0)); lm[l4] = ly.insert_layer (db::LayerProperties (104, 0)); + lm[l5] = ly.insert_layer (db::LayerProperties (105, 0)); + lm[l6] = ly.insert_layer (db::LayerProperties (106, 0)); hc.return_to_hierarchy (ly, lm); CHECKPOINT(); @@ -1158,3 +1194,9 @@ TEST(116_HierClusters) run_hc_test_with_backannotation (_this, "hc_test_l16.gds", "hc_test_au16b.gds"); } +TEST(117_HierClusters) +{ + run_hc_test (_this, "hc_test_l17.gds", "hc_test_au17.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l17.gds", "hc_test_au17b.gds"); +} + diff --git a/testdata/algo/hc_test_au17.gds b/testdata/algo/hc_test_au17.gds new file mode 100644 index 0000000000000000000000000000000000000000..d4ae2eb93aa8c5587c7e0c089d3ab8395741cdd6 GIT binary patch literal 3174 zcma);J7^S96o&tu*`1we5|a2V782tt;HsDejUpydiSdONLBuMEh=_%Vg@r{58ym5( z2$~|DA{G`F79tiwEG&Xph@=n&QLzvbHO3{^|IF;kopGIb7dBt`Z_dd*&lv>pypt&9 zyy6GsP{b~!Isyq`{Rmj}U81w6yQgvaLJx~mJzD@|R`dB)oR6Szy|NHj-#{AeVxfja4qg+?1dQ_WC z`?Q47$@!xIJiTo07n--~8eUs0=ehf4W#(j`J*?gQ` zlbTTfi|r7geInj2?VQZ80&L)<#Z*0F%`G`l+;(YS>r0$=euOe@+aFr0PCEHoGs>yG z00;`tyuTpig#+O4Sx)v(Y8COE$)QwiHMD--c-P1as zoK@tLmGBv9TMK7{Tzj84JZq^snV5AHlJ*`;B0E)>?_ruRS*or#bBL^avB>^o9|pnD zF*crdX}8VIo?WRf?UEDj)z|E@o>Xi>Dz52A^Lu>Ayc-)G55jdaq<7dp73&NK1k7EWGa z+){PY$yf6e?!6n#J=xL8+*d4BC!KtOZxHhKh$+T%k@p%sX7L@X>UQrOst zg+)+{bc$G5SfmiK2x4I&Vj+@36vQHhkf<>(8P9+3{?3egP27de7tZC}nfYHk2oVIw zP)Y^44@e=0Rp>;){U7-t9N51bXh>&w_Fmh5@Y&tZU3s@46fE<|P-{_Zz-jG3=B#@qqYYk+KnQgvan|H~y~rc1^M z%U6tq{}TA}Ksdw$1n z%uK&Ab5labZpN7JPnD_*ll(v~&*$re0JD#!gG$vSy6dOy{*C#%Lt-zKeMhmbQ1vJ` ziT;@fk}vL`%#TX^Emdb;6a38gB;#F-`Qs5#TQWweI_dgPyZf)A{ycvb^>@auUse7+ ze--&VYuB$Tf1dw;@|VMcQ=$!-C7crVGB0Y2Y1ZR7P}d*l&wsM%2(#^XmXjHvc_N+) z&FrXr8Q8!MiK+S@mFfA{m)M#72&FT&J+xGvbSultOw%a4>O3GUTmm+-N|maUuHT6) z(B8^Uz7aFu0d()UNwh1$xO!q}g)pci!I9bQPPPTwQABKa6Sx8!S9X2*QyW?Y9j6%g)YzE<&lO4Uhs zec#WRHC?BlY-yvvY^ge#=r0b*{R0r@M}d|1ELA66|B%-|=UG1om@~5ZJ!8>rsXFQU m&$)i^J|K5NOtN+^F#df?)&H3K!>(WYR{M0J{^e}RWbhLyZ!?+z literal 0 HcmV?d00001 diff --git a/testdata/algo/hc_test_l17.gds b/testdata/algo/hc_test_l17.gds new file mode 100644 index 0000000000000000000000000000000000000000..4ddd3a2a7e0cdcdcfb602c721d15571a295a20cf GIT binary patch literal 1272 zcma))F>4f26ot>enSDFajjSPHGhjf=m0+@93=)Z2L=41Y3rUqi`~hN_BCY=*V2X4R zv9w5O5kd+}3kwTD5DN=oH!&t5H|Orm_052jY%zSpd7Ss|J@>-Hxo1dguJIi;G;j@V zbc+8-oL_wW02q(zbMvq7JlWm)efQD!EqmFm`D-J-r6^WdD zvk$1Xi=XLA#8>fuzifz{HbrCuh$evgxY2ZFwSWJWNG}ypo~()-9tN7;ORN2VKGDA> zqTK%ijI0Ej-dijAUFe^+{$vu*GscVb@37IFrjy0~3x1LQg2>S?C9~^`{QYP&U0LM^ z8VS#DDgjO(m%B#O2Q0oHh5AJf?rHW)t$UDlPSXdosr2{X>%3S$nXGI6fu?h=6@Je9 zEAwt;JpKSQQxT)-_!75tO!u|fikQbXFuIWW%O|&<^0j=jL)_f?ck@(s zGv}_8AGWgT^Za>b`OP)X@cL;fvpCF*d9MQ#V;OI}W8NQZ^Lo+mxGTDw%6Nux zy$m#c$j%b)yGsnxQtyOp=4MN&PwNTy>ofOhrc|3R)%F&+&wd;^|ATw67ic Date: Tue, 8 Jan 2019 00:17:58 +0100 Subject: [PATCH 190/335] Added RBA tests for four-terminal MOS extraction plus global nets. --- src/db/db/dbNetlistDeviceExtractorClasses.h | 6 + src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 35 +++++ testdata/ruby/dbLayoutToNetlist.rb | 128 +++++++++++++++++++ 3 files changed, 169 insertions(+) diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.h b/src/db/db/dbNetlistDeviceExtractorClasses.h index 2a2b3cb35..b779317d5 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.h +++ b/src/db/db/dbNetlistDeviceExtractorClasses.h @@ -106,6 +106,12 @@ template<> struct type_traits : public typedef tl::false_tag has_default_constructor; }; +template<> struct type_traits : public tl::type_traits +{ + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + } #endif diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index 45a22e247..0c8e281c8 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -415,4 +415,39 @@ Class decl_NetlistDeviceExtractorMOS3T "This class has been introduced in version 0.26." ); +db::NetlistDeviceExtractorMOS4Transistor *make_mos4_extractor (const std::string &name) +{ + return new db::NetlistDeviceExtractorMOS4Transistor (name); +} + +Class decl_NetlistDeviceExtractorMOS4Transistor (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractorMOS4Transistor", + gsi::constructor ("new", &make_mos4_extractor, gsi::arg ("name"), + "@brief Creates a new device extractor with the given name." + ), + "@brief A device extractor for a four-terminal MOS transistor\n" + "\n" + "This class supplies the generic extractor for a MOS device.\n" + "The device is defined by two basic input layers: the diffusion area\n" + "(source and drain) and the gate area. It requires a third layer\n" + "(poly) to put the gate terminals on and a forth layer to put the bulk\n" + "terminal an. The separation between poly\n" + "and allows separating the device recognition layer (gate) from the\n" + "conductive layer.\n" + "\n" + "The bulk terminal layer can be an empty layer representing the substrate.\n" + "In this use mode the bulk terminal shapes will be produced there. This\n" + "layer then needs to be connected to a global net to establish the net.\n" + "\n" + "The device class produced by this extractor is \\DeviceClassMOS4Transistor.\n" + "The extractor extracts the four parameters of this class: L, W, AS and AD.\n" + "\n" + "The diffusion area is distributed on the number of gates connecting to\n" + "the particular source or drain area.\n" + "\n" + "This class is a closed one and methods cannot be reimplemented. To reimplement " + "specific methods, see \\DeviceExtractor.\n" + "\n" + "This class has been introduced in version 0.26." +); + } diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index 3ca02cc67..54a764c77 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -290,6 +290,134 @@ END end + def test_12_LayoutToNetlistExtractionWithDevicesAndGlobalNets + + ly = RBA::Layout::new + ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l3.gds")) + + l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, [])) + + rbulk = l2n.make_polygon_layer( ly.layer ) + rnwell = l2n.make_polygon_layer( ly.layer(1, 0) ) + ractive = l2n.make_polygon_layer( ly.layer(2, 0) ) + rpoly = l2n.make_polygon_layer( ly.layer(3, 0) ) + rpoly_lbl = l2n.make_text_layer( ly.layer(3, 1) ) + rdiff_cont = l2n.make_polygon_layer( ly.layer(4, 0) ) + rpoly_cont = l2n.make_polygon_layer( ly.layer(5, 0) ) + rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) ) + rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1) ) + rvia1 = l2n.make_polygon_layer( ly.layer(7, 0) ) + rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0) ) + rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1) ) + rpplus = l2n.make_polygon_layer( ly.layer(10, 0) ) + rnplus = l2n.make_polygon_layer( ly.layer(11, 0) ) + + ractive_in_nwell = ractive & rnwell + rpactive = ractive_in_nwell & rpplus + rntie = ractive_in_nwell & rnplus + rpgate = rpactive & rpoly + rpsd = rpactive - rpgate + + ractive_outside_nwell = ractive - rnwell + rnactive = ractive_outside_nwell & rnplus + rptie = ractive_outside_nwell & rpplus + rngate = rnactive & rpoly + rnsd = rnactive - rngate + + # PMOS transistor device extraction + pmos_ex = RBA::DeviceExtractorMOS4Transistor::new("PMOS") + l2n.extract_devices(pmos_ex, { "SD" => rpsd, "G" => rpgate, "P" => rpoly, "W" => rnwell }) + + # NMOS transistor device extraction + nmos_ex = RBA::DeviceExtractorMOS4Transistor::new("NMOS") + l2n.extract_devices(nmos_ex, { "SD" => rnsd, "G" => rngate, "P" => rpoly, "W" => rbulk }) + + # Define connectivity for netlist extraction + + # Intra-layer + l2n.connect(rpsd) + l2n.connect(rnsd) + l2n.connect(rnwell) + l2n.connect(rpoly) + l2n.connect(rdiff_cont) + l2n.connect(rpoly_cont) + l2n.connect(rmetal1) + l2n.connect(rvia1) + l2n.connect(rmetal2) + l2n.connect(rptie) + l2n.connect(rntie) + + # Inter-layer + l2n.connect(rpsd, rdiff_cont) + l2n.connect(rnsd, rdiff_cont) + l2n.connect(rpoly, rpoly_cont) + l2n.connect(rpoly_cont, rmetal1) + l2n.connect(rdiff_cont, rmetal1) + l2n.connect(rdiff_cont, rntie) + l2n.connect(rdiff_cont, rptie) + l2n.connect(rnwell, rntie) + l2n.connect(rmetal1, rvia1) + l2n.connect(rvia1, rmetal2) + l2n.connect(rpoly, rpoly_lbl) # attaches labels + l2n.connect(rmetal1, rmetal1_lbl) # attaches labels + l2n.connect(rmetal2, rmetal2_lbl) # attaches labels + + # Global connections + l2n.connect_global(rptie, "BULK") + l2n.connect_global(rbulk, "BULK") + + # Perform netlist extraction + l2n.extract_netlist + + assert_equal(l2n.netlist.to_s, < Date: Tue, 8 Jan 2019 01:09:25 +0100 Subject: [PATCH 191/335] Updated copyright. --- build.sh | 2 +- scripts/mkqtdecl.sh | 2 +- scripts/mkqtdecl_common/c++.treetop | 2 +- scripts/mkqtdecl_common/cpp_classes.rb | 2 +- scripts/mkqtdecl_common/cpp_parser_classes.rb | 2 +- scripts/mkqtdecl_common/dump.rb | 2 +- .../mkqtdecl_common/mkqtdecl_extract_ambigous_methods.rb | 2 +- scripts/mkqtdecl_common/mkqtdecl_extract_nc_pointers.rb | 2 +- .../mkqtdecl_extract_potential_factories.rb | 2 +- scripts/mkqtdecl_common/mkqtdecl_extract_props.rb | 2 +- scripts/mkqtdecl_common/mkqtdecl_extract_signals.rb | 2 +- scripts/mkqtdecl_common/parse.rb | 2 +- scripts/mkqtdecl_common/produce.rb | 8 ++++---- scripts/mkqtdecl_common/reader_ext.rb | 2 +- setup.py | 2 +- src/ant/ant/antCommon.h | 2 +- src/ant/ant/antConfig.cc | 2 +- src/ant/ant/antConfig.h | 2 +- src/ant/ant/antConfigPage.cc | 2 +- src/ant/ant/antConfigPage.h | 2 +- src/ant/ant/antForceLink.cc | 2 +- src/ant/ant/antForceLink.h | 2 +- src/ant/ant/antObject.cc | 2 +- src/ant/ant/antObject.h | 2 +- src/ant/ant/antPlugin.cc | 2 +- src/ant/ant/antPlugin.h | 2 +- src/ant/ant/antPropertiesPage.cc | 2 +- src/ant/ant/antPropertiesPage.h | 2 +- src/ant/ant/antService.cc | 2 +- src/ant/ant/antService.h | 2 +- src/ant/ant/antTemplate.cc | 2 +- src/ant/ant/antTemplate.h | 2 +- src/ant/ant/gsiDeclAnt.cc | 2 +- src/ant/unit_tests/antBasicTests.cc | 2 +- src/buddies/src/bd/bdCommon.h | 2 +- src/buddies/src/bd/bdConverterMain.cc | 2 +- src/buddies/src/bd/bdConverterMain.h | 2 +- src/buddies/src/bd/bdInit.cc | 2 +- src/buddies/src/bd/bdInit.h | 2 +- src/buddies/src/bd/bdReaderOptions.cc | 2 +- src/buddies/src/bd/bdReaderOptions.h | 2 +- src/buddies/src/bd/bdWriterOptions.cc | 2 +- src/buddies/src/bd/bdWriterOptions.h | 2 +- src/buddies/src/bd/main.cc | 2 +- src/buddies/src/bd/strm2cif.cc | 2 +- src/buddies/src/bd/strm2dxf.cc | 2 +- src/buddies/src/bd/strm2gds.cc | 2 +- src/buddies/src/bd/strm2gdstxt.cc | 2 +- src/buddies/src/bd/strm2oas.cc | 2 +- src/buddies/src/bd/strm2txt.cc | 2 +- src/buddies/src/bd/strmclip.cc | 2 +- src/buddies/src/bd/strmcmp.cc | 2 +- src/buddies/src/bd/strmrun.cc | 2 +- src/buddies/src/bd/strmxor.cc | 2 +- src/buddies/unit_tests/bdBasicTests.cc | 2 +- src/buddies/unit_tests/bdConverterTests.cc | 2 +- src/buddies/unit_tests/bdStrm2txtTests.cc | 2 +- src/buddies/unit_tests/bdStrmclipTests.cc | 2 +- src/buddies/unit_tests/bdStrmcmpTests.cc | 2 +- src/buddies/unit_tests/bdStrmrunTests.cc | 2 +- src/buddies/unit_tests/bdStrmxorTests.cc | 2 +- src/db/db/dbArray.cc | 2 +- src/db/db/dbArray.h | 2 +- src/db/db/dbAsIfFlatEdgePairs.cc | 2 +- src/db/db/dbAsIfFlatEdgePairs.h | 2 +- src/db/db/dbAsIfFlatEdges.cc | 2 +- src/db/db/dbAsIfFlatEdges.h | 2 +- src/db/db/dbAsIfFlatRegion.cc | 2 +- src/db/db/dbAsIfFlatRegion.h | 2 +- src/db/db/dbBox.cc | 2 +- src/db/db/dbBox.h | 2 +- src/db/db/dbBoxConvert.cc | 2 +- src/db/db/dbBoxConvert.h | 2 +- src/db/db/dbBoxScanner.cc | 2 +- src/db/db/dbBoxScanner.h | 2 +- src/db/db/dbBoxTree.h | 2 +- src/db/db/dbCell.cc | 2 +- src/db/db/dbCell.h | 2 +- src/db/db/dbCellGraphUtils.cc | 2 +- src/db/db/dbCellGraphUtils.h | 2 +- src/db/db/dbCellHullGenerator.cc | 2 +- src/db/db/dbCellHullGenerator.h | 2 +- src/db/db/dbCellInst.cc | 2 +- src/db/db/dbCellInst.h | 2 +- src/db/db/dbCellMapping.cc | 2 +- src/db/db/dbCellMapping.h | 2 +- src/db/db/dbClip.cc | 2 +- src/db/db/dbClip.h | 2 +- src/db/db/dbClipboard.cc | 2 +- src/db/db/dbClipboard.h | 2 +- src/db/db/dbClipboardData.cc | 2 +- src/db/db/dbClipboardData.h | 2 +- src/db/db/dbCommon.h | 2 +- src/db/db/dbCommonReader.cc | 2 +- src/db/db/dbCommonReader.h | 2 +- src/db/db/dbConverters.cc | 2 +- src/db/db/dbConverters.h | 2 +- src/db/db/dbDeepRegion.cc | 2 +- src/db/db/dbDeepRegion.h | 2 +- src/db/db/dbDeepShapeStore.cc | 2 +- src/db/db/dbDeepShapeStore.h | 2 +- src/db/db/dbEdge.cc | 2 +- src/db/db/dbEdge.h | 2 +- src/db/db/dbEdgeBoolean.cc | 2 +- src/db/db/dbEdgeBoolean.h | 2 +- src/db/db/dbEdgePair.cc | 2 +- src/db/db/dbEdgePair.h | 2 +- src/db/db/dbEdgePairRelations.cc | 2 +- src/db/db/dbEdgePairRelations.h | 2 +- src/db/db/dbEdgePairs.cc | 2 +- src/db/db/dbEdgePairs.h | 2 +- src/db/db/dbEdgePairsDelegate.cc | 2 +- src/db/db/dbEdgePairsDelegate.h | 2 +- src/db/db/dbEdgeProcessor.cc | 2 +- src/db/db/dbEdgeProcessor.h | 2 +- src/db/db/dbEdges.cc | 2 +- src/db/db/dbEdges.h | 2 +- src/db/db/dbEdgesDelegate.cc | 2 +- src/db/db/dbEdgesDelegate.h | 2 +- src/db/db/dbEdgesToContours.cc | 2 +- src/db/db/dbEdgesToContours.h | 2 +- src/db/db/dbEmptyEdgePairs.cc | 2 +- src/db/db/dbEmptyEdgePairs.h | 2 +- src/db/db/dbEmptyEdges.cc | 2 +- src/db/db/dbEmptyEdges.h | 2 +- src/db/db/dbEmptyRegion.cc | 2 +- src/db/db/dbEmptyRegion.h | 2 +- src/db/db/dbFillTool.cc | 2 +- src/db/db/dbFillTool.h | 2 +- src/db/db/dbFlatEdgePairs.cc | 2 +- src/db/db/dbFlatEdgePairs.h | 2 +- src/db/db/dbFlatEdges.cc | 2 +- src/db/db/dbFlatEdges.h | 2 +- src/db/db/dbFlatRegion.cc | 2 +- src/db/db/dbFlatRegion.h | 2 +- src/db/db/dbForceLink.cc | 2 +- src/db/db/dbForceLink.h | 2 +- src/db/db/dbFuzzyCellMapping.cc | 2 +- src/db/db/dbFuzzyCellMapping.h | 2 +- src/db/db/dbGlyphs.cc | 2 +- src/db/db/dbGlyphs.h | 2 +- src/db/db/dbHash.h | 2 +- src/db/db/dbHershey.cc | 2 +- src/db/db/dbHershey.h | 2 +- src/db/db/dbHersheyFont.h | 2 +- src/db/db/dbHierNetworkProcessor.cc | 2 +- src/db/db/dbHierNetworkProcessor.h | 2 +- src/db/db/dbHierProcessor.cc | 2 +- src/db/db/dbHierProcessor.h | 2 +- src/db/db/dbHierarchyBuilder.cc | 2 +- src/db/db/dbHierarchyBuilder.h | 2 +- src/db/db/dbInit.cc | 2 +- src/db/db/dbInit.h | 2 +- src/db/db/dbInstElement.cc | 2 +- src/db/db/dbInstElement.h | 2 +- src/db/db/dbInstances.cc | 2 +- src/db/db/dbInstances.h | 2 +- src/db/db/dbLayer.h | 2 +- src/db/db/dbLayerMapping.cc | 2 +- src/db/db/dbLayerMapping.h | 2 +- src/db/db/dbLayerProperties.cc | 2 +- src/db/db/dbLayerProperties.h | 2 +- src/db/db/dbLayout.cc | 2 +- src/db/db/dbLayout.h | 2 +- src/db/db/dbLayoutContextHandler.cc | 2 +- src/db/db/dbLayoutContextHandler.h | 2 +- src/db/db/dbLayoutDiff.cc | 2 +- src/db/db/dbLayoutDiff.h | 2 +- src/db/db/dbLayoutQuery.cc | 2 +- src/db/db/dbLayoutQuery.h | 2 +- src/db/db/dbLayoutStateModel.cc | 2 +- src/db/db/dbLayoutStateModel.h | 2 +- src/db/db/dbLayoutToNetlist.cc | 2 +- src/db/db/dbLayoutToNetlist.h | 2 +- src/db/db/dbLayoutUtils.cc | 2 +- src/db/db/dbLayoutUtils.h | 2 +- src/db/db/dbLibrary.cc | 2 +- src/db/db/dbLibrary.h | 2 +- src/db/db/dbLibraryManager.cc | 2 +- src/db/db/dbLibraryManager.h | 2 +- src/db/db/dbLibraryProxy.cc | 2 +- src/db/db/dbLibraryProxy.h | 2 +- src/db/db/dbLoadLayoutOptions.cc | 2 +- src/db/db/dbLoadLayoutOptions.h | 2 +- src/db/db/dbLocalOperation.cc | 2 +- src/db/db/dbLocalOperation.h | 2 +- src/db/db/dbManager.cc | 2 +- src/db/db/dbManager.h | 2 +- src/db/db/dbMatrix.cc | 2 +- src/db/db/dbMatrix.h | 2 +- src/db/db/dbMemStatistics.cc | 2 +- src/db/db/dbMemStatistics.h | 2 +- src/db/db/dbMetaInfo.h | 2 +- src/db/db/dbNamedLayerReader.cc | 2 +- src/db/db/dbNamedLayerReader.h | 2 +- src/db/db/dbNetlist.cc | 2 +- src/db/db/dbNetlist.h | 2 +- src/db/db/dbNetlistDeviceClasses.cc | 2 +- src/db/db/dbNetlistDeviceClasses.h | 2 +- src/db/db/dbNetlistDeviceExtractor.cc | 2 +- src/db/db/dbNetlistDeviceExtractor.h | 2 +- src/db/db/dbNetlistDeviceExtractorClasses.cc | 2 +- src/db/db/dbNetlistDeviceExtractorClasses.h | 2 +- src/db/db/dbNetlistExtractor.cc | 2 +- src/db/db/dbNetlistExtractor.h | 2 +- src/db/db/dbNetlistProperty.cc | 2 +- src/db/db/dbNetlistProperty.h | 2 +- src/db/db/dbObject.cc | 2 +- src/db/db/dbObject.h | 2 +- src/db/db/dbObjectTag.h | 2 +- src/db/db/dbObjectWithProperties.h | 2 +- src/db/db/dbOriginalLayerEdgePairs.cc | 2 +- src/db/db/dbOriginalLayerEdgePairs.h | 2 +- src/db/db/dbOriginalLayerEdges.cc | 2 +- src/db/db/dbOriginalLayerEdges.h | 2 +- src/db/db/dbOriginalLayerRegion.cc | 2 +- src/db/db/dbOriginalLayerRegion.h | 2 +- src/db/db/dbPCellDeclaration.cc | 2 +- src/db/db/dbPCellDeclaration.h | 2 +- src/db/db/dbPCellHeader.cc | 2 +- src/db/db/dbPCellHeader.h | 2 +- src/db/db/dbPCellVariant.cc | 2 +- src/db/db/dbPCellVariant.h | 2 +- src/db/db/dbPath.cc | 2 +- src/db/db/dbPath.h | 2 +- src/db/db/dbPlugin.h | 2 +- src/db/db/dbPoint.cc | 2 +- src/db/db/dbPoint.h | 2 +- src/db/db/dbPolygon.cc | 2 +- src/db/db/dbPolygon.h | 2 +- src/db/db/dbPolygonGenerators.cc | 2 +- src/db/db/dbPolygonGenerators.h | 2 +- src/db/db/dbPolygonTools.cc | 2 +- src/db/db/dbPolygonTools.h | 2 +- src/db/db/dbPropertiesRepository.cc | 2 +- src/db/db/dbPropertiesRepository.h | 2 +- src/db/db/dbReader.cc | 2 +- src/db/db/dbReader.h | 2 +- src/db/db/dbRecursiveShapeIterator.cc | 2 +- src/db/db/dbRecursiveShapeIterator.h | 2 +- src/db/db/dbRegion.cc | 2 +- src/db/db/dbRegion.h | 2 +- src/db/db/dbRegionDelegate.cc | 2 +- src/db/db/dbRegionDelegate.h | 2 +- src/db/db/dbSaveLayoutOptions.cc | 2 +- src/db/db/dbSaveLayoutOptions.h | 2 +- src/db/db/dbShape.cc | 2 +- src/db/db/dbShape.h | 2 +- src/db/db/dbShapeIterator.cc | 2 +- src/db/db/dbShapeProcessor.cc | 2 +- src/db/db/dbShapeProcessor.h | 2 +- src/db/db/dbShapeRepository.h | 2 +- src/db/db/dbShapes.cc | 2 +- src/db/db/dbShapes.h | 2 +- src/db/db/dbShapes2.cc | 2 +- src/db/db/dbShapes2.h | 2 +- src/db/db/dbShapes3.cc | 2 +- src/db/db/dbStatic.cc | 2 +- src/db/db/dbStatic.h | 2 +- src/db/db/dbStream.cc | 2 +- src/db/db/dbStream.h | 2 +- src/db/db/dbStreamLayers.cc | 2 +- src/db/db/dbStreamLayers.h | 2 +- src/db/db/dbTechnology.cc | 2 +- src/db/db/dbTechnology.h | 2 +- src/db/db/dbTestSupport.cc | 2 +- src/db/db/dbTestSupport.h | 2 +- src/db/db/dbText.cc | 2 +- src/db/db/dbText.h | 2 +- src/db/db/dbTextWriter.cc | 2 +- src/db/db/dbTextWriter.h | 2 +- src/db/db/dbTilingProcessor.cc | 2 +- src/db/db/dbTilingProcessor.h | 2 +- src/db/db/dbTrans.cc | 2 +- src/db/db/dbTrans.h | 2 +- src/db/db/dbTypes.h | 2 +- src/db/db/dbUserObject.cc | 2 +- src/db/db/dbUserObject.h | 2 +- src/db/db/dbVariableWidthPath.cc | 2 +- src/db/db/dbVariableWidthPath.h | 2 +- src/db/db/dbVector.cc | 2 +- src/db/db/dbVector.h | 2 +- src/db/db/dbWriter.cc | 2 +- src/db/db/dbWriter.h | 2 +- src/db/db/dbWriterTools.cc | 2 +- src/db/db/dbWriterTools.h | 2 +- src/db/db/gsiDeclDbBox.cc | 2 +- src/db/db/gsiDeclDbCell.cc | 2 +- src/db/db/gsiDeclDbCellMapping.cc | 2 +- src/db/db/gsiDeclDbCommonStreamOptions.cc | 2 +- src/db/db/gsiDeclDbEdge.cc | 2 +- src/db/db/gsiDeclDbEdgePair.cc | 2 +- src/db/db/gsiDeclDbEdgePairs.cc | 2 +- src/db/db/gsiDeclDbEdgeProcessor.cc | 2 +- src/db/db/gsiDeclDbEdges.cc | 2 +- src/db/db/gsiDeclDbGlyphs.cc | 2 +- src/db/db/gsiDeclDbHierNetworkProcessor.cc | 2 +- src/db/db/gsiDeclDbInstElement.cc | 2 +- src/db/db/gsiDeclDbLayerMapping.cc | 2 +- src/db/db/gsiDeclDbLayout.cc | 2 +- src/db/db/gsiDeclDbLayoutDiff.cc | 2 +- src/db/db/gsiDeclDbLayoutQuery.cc | 2 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 2 +- src/db/db/gsiDeclDbLayoutUtils.cc | 2 +- src/db/db/gsiDeclDbLibrary.cc | 2 +- src/db/db/gsiDeclDbManager.cc | 2 +- src/db/db/gsiDeclDbMatrix.cc | 2 +- src/db/db/gsiDeclDbNetlist.cc | 2 +- src/db/db/gsiDeclDbNetlistDeviceClasses.cc | 2 +- src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 2 +- src/db/db/gsiDeclDbPath.cc | 2 +- src/db/db/gsiDeclDbPoint.cc | 2 +- src/db/db/gsiDeclDbPolygon.cc | 2 +- src/db/db/gsiDeclDbReader.cc | 2 +- src/db/db/gsiDeclDbRecursiveShapeIterator.cc | 2 +- src/db/db/gsiDeclDbRegion.cc | 2 +- src/db/db/gsiDeclDbShape.cc | 2 +- src/db/db/gsiDeclDbShapeProcessor.cc | 2 +- src/db/db/gsiDeclDbShapes.cc | 2 +- src/db/db/gsiDeclDbTechnologies.cc | 2 +- src/db/db/gsiDeclDbText.cc | 2 +- src/db/db/gsiDeclDbTilingProcessor.cc | 2 +- src/db/db/gsiDeclDbTrans.cc | 2 +- src/db/db/gsiDeclDbVector.cc | 2 +- src/db/unit_tests/dbArray.cc | 2 +- src/db/unit_tests/dbBox.cc | 2 +- src/db/unit_tests/dbBoxScanner.cc | 2 +- src/db/unit_tests/dbBoxTree.cc | 2 +- src/db/unit_tests/dbCell.cc | 2 +- src/db/unit_tests/dbCellGraphUtils.cc | 2 +- src/db/unit_tests/dbCellHullGenerator.cc | 2 +- src/db/unit_tests/dbCellMapping.cc | 2 +- src/db/unit_tests/dbClip.cc | 2 +- src/db/unit_tests/dbDeepRegionTests.cc | 2 +- src/db/unit_tests/dbDeepShapeStoreTests.cc | 2 +- src/db/unit_tests/dbEdge.cc | 2 +- src/db/unit_tests/dbEdgePair.cc | 2 +- src/db/unit_tests/dbEdgePairRelations.cc | 2 +- src/db/unit_tests/dbEdgePairs.cc | 2 +- src/db/unit_tests/dbEdgeProcessor.cc | 2 +- src/db/unit_tests/dbEdges.cc | 2 +- src/db/unit_tests/dbEdgesToContours.cc | 2 +- src/db/unit_tests/dbExpression.cc | 2 +- src/db/unit_tests/dbHierNetworkProcessorTests.cc | 2 +- src/db/unit_tests/dbHierProcessorTests.cc | 2 +- src/db/unit_tests/dbHierarchyBuilderTests.cc | 2 +- src/db/unit_tests/dbLayer.cc | 2 +- src/db/unit_tests/dbLayerMapping.cc | 2 +- src/db/unit_tests/dbLayout.cc | 2 +- src/db/unit_tests/dbLayoutDiff.cc | 2 +- src/db/unit_tests/dbLayoutQuery.cc | 2 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 2 +- src/db/unit_tests/dbLayoutUtils.cc | 2 +- src/db/unit_tests/dbLibraries.cc | 2 +- src/db/unit_tests/dbLoadLayoutOptionsTests.cc | 2 +- src/db/unit_tests/dbMatrix.cc | 2 +- src/db/unit_tests/dbNetlistDeviceClassesTests.cc | 2 +- src/db/unit_tests/dbNetlistDeviceExtractorTests.cc | 2 +- src/db/unit_tests/dbNetlistExtractorTests.cc | 2 +- src/db/unit_tests/dbNetlistPropertyTests.cc | 2 +- src/db/unit_tests/dbNetlistTests.cc | 2 +- src/db/unit_tests/dbObject.cc | 2 +- src/db/unit_tests/dbPCells.cc | 2 +- src/db/unit_tests/dbPath.cc | 2 +- src/db/unit_tests/dbPoint.cc | 2 +- src/db/unit_tests/dbPolygon.cc | 2 +- src/db/unit_tests/dbPolygonTools.cc | 2 +- src/db/unit_tests/dbPropertiesRepository.cc | 2 +- src/db/unit_tests/dbRecursiveShapeIteratorTests.cc | 2 +- src/db/unit_tests/dbRegion.cc | 2 +- src/db/unit_tests/dbSaveLayoutOptionsTests.cc | 2 +- src/db/unit_tests/dbShape.cc | 2 +- src/db/unit_tests/dbShapeArray.cc | 2 +- src/db/unit_tests/dbShapeRepository.cc | 2 +- src/db/unit_tests/dbShapes.cc | 2 +- src/db/unit_tests/dbStreamLayers.cc | 2 +- src/db/unit_tests/dbText.cc | 2 +- src/db/unit_tests/dbTilingProcessor.cc | 2 +- src/db/unit_tests/dbTrans.cc | 2 +- src/db/unit_tests/dbVariableWidthPath.cc | 2 +- src/db/unit_tests/dbVector.cc | 2 +- src/db/unit_tests/dbWriterTools.cc | 2 +- src/drc/drc/drcCommon.h | 2 +- src/drc/drc/drcForceLink.cc | 2 +- src/drc/drc/drcForceLink.h | 2 +- src/drc/unit_tests/drcBasicTests.cc | 2 +- src/drc/unit_tests/drcSimpleTests.cc | 2 +- src/drc/unit_tests/drcSuiteTests.cc | 2 +- src/edt/edt/edtCommon.h | 2 +- src/edt/edt/edtConfig.cc | 2 +- src/edt/edt/edtConfig.h | 2 +- src/edt/edt/edtDialogs.cc | 2 +- src/edt/edt/edtDialogs.h | 2 +- src/edt/edt/edtEditorOptionsPages.cc | 2 +- src/edt/edt/edtEditorOptionsPages.h | 2 +- src/edt/edt/edtInstPropertiesPage.cc | 2 +- src/edt/edt/edtInstPropertiesPage.h | 2 +- src/edt/edt/edtMainService.cc | 2 +- src/edt/edt/edtMainService.h | 2 +- src/edt/edt/edtPCellParametersPage.cc | 2 +- src/edt/edt/edtPCellParametersPage.h | 2 +- src/edt/edt/edtPartialService.cc | 2 +- src/edt/edt/edtPartialService.h | 2 +- src/edt/edt/edtPlugin.cc | 2 +- src/edt/edt/edtPlugin.h | 2 +- src/edt/edt/edtPropertiesPageUtils.cc | 2 +- src/edt/edt/edtPropertiesPageUtils.h | 2 +- src/edt/edt/edtPropertiesPages.cc | 2 +- src/edt/edt/edtPropertiesPages.h | 2 +- src/edt/edt/edtService.cc | 2 +- src/edt/edt/edtService.h | 2 +- src/edt/edt/edtServiceImpl.cc | 2 +- src/edt/edt/edtServiceImpl.h | 2 +- src/edt/edt/edtUtils.cc | 2 +- src/edt/edt/edtUtils.h | 2 +- src/edt/edt/gsiDeclEdt.cc | 2 +- src/edt/unit_tests/edtBasicTests.cc | 2 +- src/fontgen/fontgen.cc | 2 +- src/gsi/gsi/gsi.cc | 2 +- src/gsi/gsi/gsi.h | 2 +- src/gsi/gsi/gsiCallback.h | 2 +- src/gsi/gsi/gsiCallbackVar.h | 2 +- src/gsi/gsi/gsiClass.cc | 2 +- src/gsi/gsi/gsiClass.h | 2 +- src/gsi/gsi/gsiClassBase.cc | 2 +- src/gsi/gsi/gsiClassBase.h | 2 +- src/gsi/gsi/gsiCommon.h | 2 +- src/gsi/gsi/gsiDecl.h | 2 +- src/gsi/gsi/gsiDeclBasic.cc | 2 +- src/gsi/gsi/gsiDeclBasic.h | 2 +- src/gsi/gsi/gsiDeclInternal.cc | 2 +- src/gsi/gsi/gsiDeclTl.cc | 2 +- src/gsi/gsi/gsiEnums.h | 2 +- src/gsi/gsi/gsiExpression.cc | 2 +- src/gsi/gsi/gsiExpression.h | 2 +- src/gsi/gsi/gsiExternalMain.cc | 2 +- src/gsi/gsi/gsiExternalMain.h | 2 +- src/gsi/gsi/gsiInspector.cc | 2 +- src/gsi/gsi/gsiInspector.h | 2 +- src/gsi/gsi/gsiInterpreter.cc | 2 +- src/gsi/gsi/gsiInterpreter.h | 2 +- src/gsi/gsi/gsiIterators.h | 2 +- src/gsi/gsi/gsiMethods.cc | 2 +- src/gsi/gsi/gsiMethods.h | 2 +- src/gsi/gsi/gsiMethodsVar.h | 2 +- src/gsi/gsi/gsiObject.cc | 2 +- src/gsi/gsi/gsiObject.h | 2 +- src/gsi/gsi/gsiObjectHolder.cc | 2 +- src/gsi/gsi/gsiObjectHolder.h | 2 +- src/gsi/gsi/gsiSerialisation.cc | 2 +- src/gsi/gsi/gsiSerialisation.h | 2 +- src/gsi/gsi/gsiSignals.cc | 2 +- src/gsi/gsi/gsiSignals.h | 2 +- src/gsi/gsi/gsiTypes.cc | 2 +- src/gsi/gsi/gsiTypes.h | 2 +- src/gsi/gsi_test/gsiTest.cc | 2 +- src/gsi/gsi_test/gsiTest.h | 2 +- src/gsi/gsi_test/gsiTestForceLink.h | 2 +- src/gsi/unit_tests/gsiExpression.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQAbstractItemModel.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQAbstractListModel.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQAbstractTableModel.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQBasicTimer.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQBuffer.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQByteArrayMatcher.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQChildEvent.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQCoreApplication.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQCryptographicHash.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQDataStream.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQDate.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQDateTime.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQDir.cc | 2 +- .../qt4/QtCore/gsiDeclQDynamicPropertyChangeEvent.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQEasingCurve.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQEvent.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQEventLoop.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQFactoryInterface.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQFile.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQFileInfo.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQFileSystemWatcher.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQIODevice.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQLibrary.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQLibraryInfo.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQLine.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQLineF.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQLocale.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQMargins.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQMetaClassInfo.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQMetaEnum.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQMetaMethod.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQMetaObject.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQMetaProperty.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQMetaType.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQMimeData.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQModelIndex.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQMutex.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQObject.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQPersistentModelIndex.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQPluginLoader.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQPoint.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQPointF.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQProcess.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQProcessEnvironment.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQReadLocker.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQReadWriteLock.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQRect.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQRectF.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQRegExp.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQResource.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQSemaphore.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQSettings.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQSignalMapper.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQSize.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQSizeF.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQSocketNotifier.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQStringMatcher.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQSysInfo.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQSystemLocale.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTemporaryFile.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTextCodec.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTextCodec_ConverterState.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTextDecoder.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTextEncoder.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTextStream.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQThread.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTime.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTimeLine.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTimer.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTimerEvent.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQTranslator.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQUrl.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQWaitCondition.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQWriteLocker.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQt.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQtCoreAdd.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQtCoreTypeTraits.h | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQt_1.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQt_2.cc | 2 +- src/gsiqt/qt4/QtCore/gsiDeclQt_3.cc | 2 +- src/gsiqt/qt4/QtCore/gsiQtExternals.h | 2 +- src/gsiqt/qt4/QtDesigner/gsiDeclQAbstractFormBuilder.cc | 2 +- src/gsiqt/qt4/QtDesigner/gsiDeclQFormBuilder.cc | 2 +- src/gsiqt/qt4/QtDesigner/gsiDeclQtDesignerTypeTraits.h | 2 +- src/gsiqt/qt4/QtDesigner/gsiQtExternals.h | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractButton.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractGraphicsShapeItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractItemDelegate.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractItemView.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractPageSetupDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractPrintDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractProxyModel.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractScrollArea.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractSlider.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractSpinBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout.cc | 2 +- .../gsiDeclQAbstractTextDocumentLayout_PaintContext.cc | 2 +- .../QtGui/gsiDeclQAbstractTextDocumentLayout_Selection.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAbstractUndoItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAccessible.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAccessibleApplication.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAccessibleEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAccessibleInterface.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAccessibleObject.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAccessibleWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQAction.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQActionEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQActionGroup.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQApplication.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQBitmap.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQBoxLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQBrush.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQButtonGroup.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQCDEStyle.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQCalendarWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQCheckBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQCleanlooksStyle.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQClipboard.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQClipboardEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQCloseEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQColor.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQColorDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQColormap.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQColumnView.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQComboBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQCommandLinkButton.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQCommonStyle.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQCompleter.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQConicalGradient.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQContextMenuEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQCursor.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDataWidgetMapper.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDateEdit.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDateTimeEdit.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDesktopServices.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDesktopWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDial.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDialogButtonBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDirIterator.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDirModel.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDockWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDoubleSpinBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDoubleValidator.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDrag.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDragEnterEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDragLeaveEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDragMoveEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDragResponseEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQDropEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQErrorMessage.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFileDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFileIconProvider.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFileOpenEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFileSystemModel.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFocusEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFocusFrame.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFont.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFontComboBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFontDatabase.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFontDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFontInfo.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFontMetrics.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFontMetricsF.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFormLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQFrame.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGesture.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGestureEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGestureRecognizer.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGradient.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsAnchor.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsAnchorLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsBlurEffect.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsColorizeEffect.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsDropShadowEffect.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsEffect.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsEllipseItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsGridLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItemAnimation.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItemGroup.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLayoutItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLineItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLinearLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsObject.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsOpacityEffect.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPathItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPixmapItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPolygonItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsProxyWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsRectItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsRotation.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsScale.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsScene.cc | 2 +- .../qt4/QtGui/gsiDeclQGraphicsSceneContextMenuEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneDragDropEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneHelpEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneHoverEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneMouseEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneMoveEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneResizeEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneWheelEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSimpleTextItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsTextItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsTransform.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsView.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGraphicsWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGridLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQGroupBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQHBoxLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQHeaderView.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQHelpEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQHideEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQHoverEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQIcon.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQIconDragEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQIconEngine.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQIconEnginePlugin.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQIconEnginePluginV2.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQIconEngineV2.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQImage.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQImageIOHandler.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQImageIOPlugin.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQImageReader.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQImageTextKeyLang.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQImageWriter.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQInputContext.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQInputContextFactory.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQInputContextPlugin.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQInputDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQInputEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQInputMethodEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQInputMethodEvent_Attribute.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQIntValidator.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQItemDelegate.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQItemEditorCreatorBase.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQItemEditorFactory.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQItemSelection.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQItemSelectionModel.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQItemSelectionRange.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQKeyEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQKeySequence.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQLCDNumber.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQLabel.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQLayoutItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQLineEdit.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQLinearGradient.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQListView.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQListWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQListWidgetItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMainWindow.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMatrix.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMatrix4x4.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMdiArea.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMdiSubWindow.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMenu.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMenuBar.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMessageBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMimeSource.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMotifStyle.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMouseEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMoveEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQMovie.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPageSetupDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPaintDevice.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPaintEngine.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPaintEngineState.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPaintEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPainter.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPainterPath.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPainterPathStroker.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPainterPath_Element.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPalette.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPanGesture.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPen.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPicture.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPinchGesture.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPixmap.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPixmapCache.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPlainTextDocumentLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPlainTextEdit.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPlastiqueStyle.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPolygon.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPolygonF.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPrintDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPrintEngine.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPrintPreviewDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPrintPreviewWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPrinter.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPrinterInfo.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQProgressBar.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQProgressDialog.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQPushButton.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQQuaternion.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQRadialGradient.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQRadioButton.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQRegExpValidator.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQRegion.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQResizeEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQRubberBand.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQScrollArea.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQScrollBar.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQShortcut.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQShortcutEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQShowEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSizeGrip.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSizePolicy.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSlider.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSortFilterProxyModel.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSound.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSpacerItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSpinBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSplashScreen.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSplitter.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSplitterHandle.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStackedLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStackedWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStandardItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStandardItemModel.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStatusBar.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStatusTipEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStringListModel.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyle.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleFactory.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturn.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturnMask.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturnVariant.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOption.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionButton.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionComboBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionComplex.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionDockWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFocusRect.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrame.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrameV2.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrameV3.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionGraphicsItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionGroupBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionHeader.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionMenuItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionProgressBar.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionProgressBarV2.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3DockWindow.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3ListView.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3ListViewItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionRubberBand.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSizeGrip.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSlider.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSpinBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTab.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabBarBase.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabBarBaseV2.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabV2.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabV3.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabWidgetFrame.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTitleBar.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBar.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBoxV2.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolButton.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV2.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV3.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV4.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStylePainter.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStylePlugin.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQStyledItemDelegate.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSwipeGesture.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSyntaxHighlighter.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQSystemTrayIcon.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTabBar.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTabWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTableView.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTableWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTableWidgetItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTableWidgetSelectionRange.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTabletEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTapAndHoldGesture.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTapGesture.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextBlock.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextBlockFormat.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextBlockGroup.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextBlockUserData.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextBlock_Iterator.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextBrowser.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextCharFormat.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextCursor.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextDocument.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextDocumentFragment.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextDocumentWriter.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextEdit.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextEdit_ExtraSelection.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextFormat.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextFragment.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextFrame.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextFrameFormat.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextFrame_Iterator.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextImageFormat.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextInlineObject.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextLayout_FormatRange.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextLength.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextLine.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextList.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextListFormat.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextObject.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextObjectInterface.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextOption.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextOption_Tab.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextTable.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextTableCell.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextTableCellFormat.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTextTableFormat.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTimeEdit.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQToolBar.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQToolBarChangeEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQToolBox.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQToolButton.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQToolTip.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTouchEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTouchEvent_TouchPoint.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTransform.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTreeView.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTreeWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTreeWidgetItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQTreeWidgetItemIterator.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQUndoCommand.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQUndoGroup.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQUndoStack.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQUndoView.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQUnixPrintWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQVBoxLayout.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQValidator.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQVector2D.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQVector3D.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQVector4D.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQWhatsThis.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQWhatsThisClickedEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQWheelEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQWidget.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQWidgetAction.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQWidgetItem.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQWindowStateChangeEvent.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQWindowsStyle.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQWizard.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQWizardPage.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQtGuiAdd.cc | 2 +- src/gsiqt/qt4/QtGui/gsiDeclQtGuiTypeTraits.h | 2 +- src/gsiqt/qt4/QtGui/gsiQtExternals.h | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQAbstractNetworkCache.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQAbstractSocket.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQAuthenticator.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQFtp.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQHostAddress.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQHostInfo.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQIPv6Address.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQLocalServer.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQLocalSocket.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkAccessManager.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkAddressEntry.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCacheMetaData.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCookie.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCookieJar.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkDiskCache.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkInterface.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxy.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxyFactory.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxyQuery.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkReply.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkRequest.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQSsl.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQSslCertificate.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQSslCipher.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQSslConfiguration.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQSslError.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQSslKey.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQSslSocket.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQTcpServer.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQTcpSocket.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQUdpSocket.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQUrlInfo.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQtNetworkAdd.cc | 2 +- src/gsiqt/qt4/QtNetwork/gsiDeclQtNetworkTypeTraits.h | 2 +- src/gsiqt/qt4/QtNetwork/gsiQtExternals.h | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSql.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlDatabase.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlDriver.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlDriverCreatorBase.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlError.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlField.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlIndex.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlQuery.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlQueryModel.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlRecord.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlRelation.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlRelationalTableModel.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlResult.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQSqlTableModel.cc | 2 +- src/gsiqt/qt4/QtSql/gsiDeclQtSqlTypeTraits.h | 2 +- src/gsiqt/qt4/QtSql/gsiQtExternals.h | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomAttr.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomCDATASection.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomCharacterData.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomComment.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomDocument.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomDocumentFragment.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomDocumentType.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomElement.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomEntity.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomEntityReference.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomImplementation.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomNamedNodeMap.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomNode.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomNodeList.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomNotation.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomProcessingInstruction.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQDomText.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlAttributes.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlContentHandler.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlDTDHandler.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlDeclHandler.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlDefaultHandler.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlEntityResolver.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlErrorHandler.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlInputSource.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlLexicalHandler.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlLocator.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlNamespaceSupport.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlParseException.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlReader.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQXmlSimpleReader.cc | 2 +- src/gsiqt/qt4/QtXml/gsiDeclQtXmlTypeTraits.h | 2 +- src/gsiqt/qt4/QtXml/gsiQtExternals.h | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAbstractAnimation.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAbstractEventDispatcher.cc | 2 +- .../QtCore/gsiDeclQAbstractEventDispatcher_TimerInfo.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAbstractItemModel.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAbstractListModel.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAbstractNativeEventFilter.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAbstractProxyModel.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAbstractState.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAbstractTableModel.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAbstractTransition.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAnimationDriver.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAnimationGroup.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQAssociativeIterable.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQBasicMutex.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQBasicTimer.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQBuffer.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQByteArrayDataPtr.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQByteArrayMatcher.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQChildEvent.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQCollator.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQCollatorSortKey.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQCommandLineOption.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQCommandLineParser.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQCoreApplication.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQCryptographicHash.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQDataStream.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQDate.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQDateTime.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQDebug.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQDebugStateSaver.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQDeferredDeleteEvent.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQDir.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQDirIterator.cc | 2 +- .../qt5/QtCore/gsiDeclQDynamicPropertyChangeEvent.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQEasingCurve.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQElapsedTimer.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQEvent.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQEventLoop.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQEventLoopLocker.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQEventTransition.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQFactoryInterface.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQFile.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQFileDevice.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQFileInfo.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQFileSelector.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQFileSystemWatcher.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQFinalState.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQHistoryState.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQIODevice.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQIdentityProxyModel.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQItemSelection.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQItemSelectionModel.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQItemSelectionRange.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonArray.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonArray_Const_iterator.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonArray_Iterator.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonDocument.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonObject.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonObject_Const_iterator.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonObject_Iterator.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonParseError.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonValue.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonValuePtr.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonValueRef.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQJsonValueRefPtr.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQLibrary.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQLibraryInfo.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQLine.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQLineF.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQLocale.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQLockFile.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQLoggingCategory.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMapDataBase.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMapNodeBase.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMargins.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMarginsF.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMessageAuthenticationCode.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMessageLogContext.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMessageLogger.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMetaClassInfo.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMetaEnum.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMetaMethod.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMetaObject.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMetaObject_Connection.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMetaProperty.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMimeData.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMimeDatabase.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMimeType.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQModelIndex.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQMutex.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQNoDebug.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQObject.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQParallelAnimationGroup.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQPauseAnimation.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQPersistentModelIndex.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQPluginLoader.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQPoint.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQPointF.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQProcess.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQProcessEnvironment.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQPropertyAnimation.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQReadLocker.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQReadWriteLock.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQRect.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQRectF.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQRegExp.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQRegularExpression.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQRegularExpressionMatch.cc | 2 +- .../qt5/QtCore/gsiDeclQRegularExpressionMatchIterator.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQResource.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQRunnable.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSaveFile.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSemaphore.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSequentialAnimationGroup.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSequentialIterable.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSettings.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSharedMemory.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSignalBlocker.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSignalMapper.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSignalTransition.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSize.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSizeF.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSocketNotifier.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSortFilterProxyModel.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQStandardPaths.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQState.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQStateMachine.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQStateMachine_SignalEvent.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQStateMachine_WrappedEvent.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQStaticPlugin.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQStorageInfo.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQStringDataPtr.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQStringListModel.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQStringMatcher.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSysInfo.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQSystemSemaphore.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTemporaryDir.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTemporaryFile.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTextBoundaryFinder.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTextCodec.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTextCodec_ConverterState.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTextDecoder.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTextEncoder.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTextStream.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQThread.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQThreadPool.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTime.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTimeLine.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTimeZone.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTimeZone_OffsetData.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTimer.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTimerEvent.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQTranslator.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQUrl.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQUrlQuery.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQVariantAnimation.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQWaitCondition.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQWriteLocker.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamAttribute.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamAttributes.cc | 2 +- .../qt5/QtCore/gsiDeclQXmlStreamEntityDeclaration.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamEntityResolver.cc | 2 +- .../qt5/QtCore/gsiDeclQXmlStreamNamespaceDeclaration.cc | 2 +- .../qt5/QtCore/gsiDeclQXmlStreamNotationDeclaration.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamReader.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamStringRef.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamWriter.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQt.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQtCoreAdd.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQtCoreTypeTraits.h | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQt_1.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQt_2.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQt_3.cc | 2 +- src/gsiqt/qt5/QtCore/gsiDeclQt_4.cc | 2 +- src/gsiqt/qt5/QtCore/gsiQtExternals.h | 2 +- .../qt5/QtDesigner/gsiDeclQAbstractExtensionFactory.cc | 2 +- .../qt5/QtDesigner/gsiDeclQAbstractExtensionManager.cc | 2 +- src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractFormBuilder.cc | 2 +- src/gsiqt/qt5/QtDesigner/gsiDeclQFormBuilder.cc | 2 +- src/gsiqt/qt5/QtDesigner/gsiDeclQtDesignerTypeTraits.h | 2 +- src/gsiqt/qt5/QtDesigner/gsiQtExternals.h | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout.cc | 2 +- .../gsiDeclQAbstractTextDocumentLayout_PaintContext.cc | 2 +- .../QtGui/gsiDeclQAbstractTextDocumentLayout_Selection.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAbstractUndoItem.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessible.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleActionInterface.cc | 2 +- .../qt5/QtGui/gsiDeclQAccessibleEditableTextInterface.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleImageInterface.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleInterface.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleObject.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleStateChangeEvent.cc | 2 +- .../qt5/QtGui/gsiDeclQAccessibleTableCellInterface.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableInterface.cc | 2 +- .../qt5/QtGui/gsiDeclQAccessibleTableModelChangeEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextCursorEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextInsertEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextInterface.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextRemoveEvent.cc | 2 +- .../qt5/QtGui/gsiDeclQAccessibleTextSelectionEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextUpdateEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleValueChangeEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessibleValueInterface.cc | 2 +- .../qt5/QtGui/gsiDeclQAccessible_ActivationObserver.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQAccessible_State.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQActionEvent.cc | 2 +- .../qt5/QtGui/gsiDeclQApplicationStateChangeEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQBackingStore.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQBitmap.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQBrush.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQClipboard.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQCloseEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQColor.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQConicalGradient.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQContextMenuEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQCursor.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQDesktopServices.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQDoubleValidator.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQDrag.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQDragEnterEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQDragLeaveEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQDragMoveEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQDropEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQEnterEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQExposeEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQFileOpenEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQFocusEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQFont.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQFontDatabase.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQFontInfo.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQFontMetrics.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQFontMetricsF.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQGenericPlugin.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQGenericPluginFactory.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQGlyphRun.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQGradient.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQGuiApplication.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQHelpEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQHideEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQHoverEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQIcon.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQIconDragEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQIconEngine.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQIconEnginePlugin.cc | 2 +- .../QtGui/gsiDeclQIconEngine_AvailableSizesArgument.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQImage.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQImageIOHandler.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQImageIOPlugin.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQImageReader.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQImageWriter.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQInputEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQInputMethod.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQInputMethodEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQInputMethodEvent_Attribute.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQInputMethodQueryEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQIntValidator.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQKeyEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQKeySequence.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQLinearGradient.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQMatrix.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQMatrix4x4.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQMouseEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQMoveEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQMovie.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQNativeGestureEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQOffscreenSurface.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPageLayout.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPageSize.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPagedPaintDevice.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPagedPaintDevice_Margins.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPaintDevice.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPaintDeviceWindow.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPaintEngine.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPaintEngineState.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPaintEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPainter.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPainterPath.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPainterPathStroker.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPainterPath_Element.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPainter_PixmapFragment.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPalette.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPdfWriter.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPen.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPicture.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPictureFormatPlugin.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPixelFormat.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPixmap.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPixmapCache.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPlatformSurfaceEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPolygon.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQPolygonF.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQQuaternion.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQRadialGradient.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQRasterWindow.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQRawFont.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQRegExpValidator.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQRegion.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQRegularExpressionValidator.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQResizeEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQScreen.cc | 2 +- .../qt5/QtGui/gsiDeclQScreenOrientationChangeEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQScrollEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQScrollPrepareEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQSessionManager.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQShortcutEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQShowEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQStandardItem.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQStandardItemModel.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQStaticText.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQStatusTipEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQStyleHints.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQSurface.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQSurfaceFormat.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQSyntaxHighlighter.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTabletEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextBlock.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextBlockFormat.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextBlockGroup.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextBlockUserData.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextBlock_Iterator.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextCharFormat.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextCursor.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextDocument.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextDocumentFragment.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextDocumentWriter.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextFormat.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextFragment.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextFrame.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextFrameFormat.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextFrame_Iterator.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextImageFormat.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextInlineObject.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextItem.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextLayout.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextLayout_FormatRange.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextLength.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextLine.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextList.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextListFormat.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextObject.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextObjectInterface.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextOption.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextOption_Tab.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextTable.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextTableCell.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextTableCellFormat.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTextTableFormat.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQToolBarChangeEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTouchDevice.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTouchEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTouchEvent_TouchPoint.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQTransform.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQValidator.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQVector2D.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQVector3D.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQVector4D.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQWhatsThisClickedEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQWheelEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQWindow.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQWindowStateChangeEvent.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQtGuiAdd.cc | 2 +- src/gsiqt/qt5/QtGui/gsiDeclQtGuiTypeTraits.h | 2 +- src/gsiqt/qt5/QtGui/gsiQtExternals.h | 2 +- .../qt5/QtMultimedia/gsiDeclQAbstractAudioDeviceInfo.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioInput.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioOutput.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQAbstractNetworkCache.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractSocket.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoBuffer.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoFilter.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQAbstractVideoSurface.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudio.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioBuffer.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDecoder.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDecoderControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDeviceInfo.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQAudioEncoderSettings.cc | 2 +- .../QtMultimedia/gsiDeclQAudioEncoderSettingsControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioFormat.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioInput.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQAudioInputSelectorControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioOutput.cc | 2 +- .../QtMultimedia/gsiDeclQAudioOutputSelectorControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioProbe.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioRecorder.cc | 2 +- .../QtMultimedia/gsiDeclQAudioSystemFactoryInterface.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemPlugin.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQAuthenticator.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCamera.cc | 2 +- .../gsiDeclQCameraCaptureBufferFormatControl.cc | 2 +- .../gsiDeclQCameraCaptureDestinationControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraExposure.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQCameraExposureControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQCameraFeedbackControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFlashControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocus.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocusControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocusZone.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCapture.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQCameraImageCaptureControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQCameraImageProcessing.cc | 2 +- .../QtMultimedia/gsiDeclQCameraImageProcessingControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraInfo.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraInfoControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraLocksControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQCameraViewfinderSettings.cc | 2 +- .../gsiDeclQCameraViewfinderSettingsControl.cc | 2 +- .../gsiDeclQCameraViewfinderSettingsControl2.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraZoomControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQCamera_FrameRateRange.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsDomainNameRecord.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQDnsHostAddressRecord.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsLookup.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQDnsMailExchangeRecord.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsServiceRecord.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsTextRecord.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQGraphicsVideoItem.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQHostAddress.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQHostInfo.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQHttpMultiPart.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQHttpPart.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQIPv6Address.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQImageEncoderControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQImageEncoderSettings.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQLocalServer.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQLocalSocket.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQMediaAudioProbeControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQMediaAvailabilityControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQMediaBindableInterface.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQMediaContainerControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaContent.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaControl.cc | 2 +- .../QtMultimedia/gsiDeclQMediaGaplessPlaybackControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaMetaData.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQMediaNetworkAccessControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaObject.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlayer.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlayerControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlaylist.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorder.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQMediaRecorderControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaResource.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaService.cc | 2 +- .../gsiDeclQMediaServiceCameraInfoInterface.cc | 2 +- .../gsiDeclQMediaServiceDefaultDeviceInterface.cc | 2 +- .../QtMultimedia/gsiDeclQMediaServiceFeaturesInterface.cc | 2 +- .../gsiDeclQMediaServiceProviderFactoryInterface.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQMediaServiceProviderHint.cc | 2 +- .../QtMultimedia/gsiDeclQMediaServiceProviderPlugin.cc | 2 +- .../gsiDeclQMediaServiceSupportedDevicesInterface.cc | 2 +- .../gsiDeclQMediaServiceSupportedFormatsInterface.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaStreamsControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaTimeInterval.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaTimeRange.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQMediaVideoProbeControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQMetaDataReaderControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQMetaDataWriterControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQMultimedia.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQNetworkAccessManager.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkAddressEntry.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQNetworkCacheMetaData.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQNetworkConfiguration.cc | 2 +- .../QtMultimedia/gsiDeclQNetworkConfigurationManager.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCookie.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCookieJar.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkDiskCache.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkInterface.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxy.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxyFactory.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxyQuery.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkReply.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkRequest.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkSession.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioData.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioDataControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioTuner.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioTunerControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQSound.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQSoundEffect.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQSsl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCertificate.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQSslCertificateExtension.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCipher.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQSslConfiguration.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQSslEllipticCurve.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQSslError.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQSslKey.cc | 2 +- .../QtMultimedia/gsiDeclQSslPreSharedKeyAuthenticator.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQSslSocket.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQTcpServer.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQTcpSocket.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQUdpSocket.cc | 2 +- .../QtMultimedia/gsiDeclQVideoDeviceSelectorControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQVideoEncoderSettings.cc | 2 +- .../QtMultimedia/gsiDeclQVideoEncoderSettingsControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoFilterRunnable.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoFrame.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoProbe.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQVideoRendererControl.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoSurfaceFormat.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWidget.cc | 2 +- src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWindowControl.cc | 2 +- .../qt5/QtMultimedia/gsiDeclQtMultimediaTypeTraits.h | 2 +- src/gsiqt/qt5/QtMultimedia/gsiQtExternals.h | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQAbstractNetworkCache.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQAbstractSocket.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQAuthenticator.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQDnsDomainNameRecord.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQDnsHostAddressRecord.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQDnsLookup.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQDnsMailExchangeRecord.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQDnsServiceRecord.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQDnsTextRecord.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQHostAddress.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQHostInfo.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQHttpMultiPart.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQHttpPart.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQIPv6Address.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQLocalServer.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQLocalSocket.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkAccessManager.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkAddressEntry.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCacheMetaData.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkConfiguration.cc | 2 +- .../qt5/QtNetwork/gsiDeclQNetworkConfigurationManager.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCookie.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCookieJar.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkDiskCache.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkInterface.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxy.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxyFactory.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxyQuery.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkReply.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkRequest.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkSession.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQSsl.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQSslCertificate.cc | 2 +- .../qt5/QtNetwork/gsiDeclQSslCertificateExtension.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQSslCipher.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQSslConfiguration.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQSslEllipticCurve.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQSslError.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQSslKey.cc | 2 +- .../qt5/QtNetwork/gsiDeclQSslPreSharedKeyAuthenticator.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQSslSocket.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQTcpServer.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQTcpSocket.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQUdpSocket.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQtNetworkAdd.cc | 2 +- src/gsiqt/qt5/QtNetwork/gsiDeclQtNetworkTypeTraits.h | 2 +- src/gsiqt/qt5/QtNetwork/gsiQtExternals.h | 2 +- .../qt5/QtPrintSupport/gsiDeclQAbstractPrintDialog.cc | 2 +- src/gsiqt/qt5/QtPrintSupport/gsiDeclQPageSetupDialog.cc | 2 +- src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintDialog.cc | 2 +- src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintEngine.cc | 2 +- .../qt5/QtPrintSupport/gsiDeclQPrintPreviewDialog.cc | 2 +- .../qt5/QtPrintSupport/gsiDeclQPrintPreviewWidget.cc | 2 +- src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrinter.cc | 2 +- src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrinterInfo.cc | 2 +- .../qt5/QtPrintSupport/gsiDeclQtPrintSupportTypeTraits.h | 2 +- src/gsiqt/qt5/QtPrintSupport/gsiQtExternals.h | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSql.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlDatabase.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlDriver.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlDriverCreatorBase.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlError.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlField.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlIndex.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlQuery.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlQueryModel.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlRecord.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlRelation.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlRelationalTableModel.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlResult.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQSqlTableModel.cc | 2 +- src/gsiqt/qt5/QtSql/gsiDeclQtSqlTypeTraits.h | 2 +- src/gsiqt/qt5/QtSql/gsiQtExternals.h | 2 +- src/gsiqt/qt5/QtSvg/gsiDeclQGraphicsSvgItem.cc | 2 +- src/gsiqt/qt5/QtSvg/gsiDeclQSvgGenerator.cc | 2 +- src/gsiqt/qt5/QtSvg/gsiDeclQSvgRenderer.cc | 2 +- src/gsiqt/qt5/QtSvg/gsiDeclQSvgWidget.cc | 2 +- src/gsiqt/qt5/QtSvg/gsiDeclQtSvgTypeTraits.h | 2 +- src/gsiqt/qt5/QtSvg/gsiQtExternals.h | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractButton.cc | 2 +- .../qt5/QtWidgets/gsiDeclQAbstractGraphicsShapeItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractItemDelegate.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractItemView.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractScrollArea.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractSlider.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractSpinBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQAccessibleWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQAction.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQActionGroup.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQApplication.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQBoxLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQButtonGroup.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQCalendarWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQCheckBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQColorDialog.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQColormap.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQColumnView.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQComboBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQCommandLinkButton.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQCommonStyle.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQCompleter.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQDataWidgetMapper.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQDateEdit.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQDateTimeEdit.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQDesktopWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQDial.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQDialog.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQDialogButtonBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQDirModel.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQDockWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQDoubleSpinBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQErrorMessage.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQFileDialog.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQFileIconProvider.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQFileSystemModel.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQFocusFrame.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQFontComboBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQFontDialog.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQFormLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQFrame.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGesture.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGestureEvent.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGestureRecognizer.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsAnchor.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsAnchorLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsBlurEffect.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsColorizeEffect.cc | 2 +- .../qt5/QtWidgets/gsiDeclQGraphicsDropShadowEffect.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsEffect.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsEllipseItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsGridLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItemAnimation.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItemGroup.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLayoutItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLineItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLinearLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsObject.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsOpacityEffect.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPathItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPixmapItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPolygonItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsProxyWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsRectItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsRotation.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsScale.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsScene.cc | 2 +- .../QtWidgets/gsiDeclQGraphicsSceneContextMenuEvent.cc | 2 +- .../qt5/QtWidgets/gsiDeclQGraphicsSceneDragDropEvent.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneEvent.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneHelpEvent.cc | 2 +- .../qt5/QtWidgets/gsiDeclQGraphicsSceneHoverEvent.cc | 2 +- .../qt5/QtWidgets/gsiDeclQGraphicsSceneMouseEvent.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneMoveEvent.cc | 2 +- .../qt5/QtWidgets/gsiDeclQGraphicsSceneResizeEvent.cc | 2 +- .../qt5/QtWidgets/gsiDeclQGraphicsSceneWheelEvent.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSimpleTextItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsTextItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsTransform.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsView.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGridLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQGroupBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQHBoxLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQHeaderView.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQInputDialog.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQItemDelegate.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQItemEditorCreatorBase.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQItemEditorFactory.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQKeySequenceEdit.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQLCDNumber.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQLabel.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQLayoutItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQLineEdit.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQListView.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQListWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQListWidgetItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQMainWindow.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQMdiArea.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQMdiSubWindow.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQMenu.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQMenuBar.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQMessageBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQPanGesture.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQPinchGesture.cc | 2 +- .../qt5/QtWidgets/gsiDeclQPlainTextDocumentLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQPlainTextEdit.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQProgressBar.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQProgressDialog.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQPushButton.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQRadioButton.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQRubberBand.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQScrollArea.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQScrollBar.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQScroller.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQScrollerProperties.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQShortcut.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQSizeGrip.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQSizePolicy.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQSlider.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQSpacerItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQSpinBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQSplashScreen.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQSplitter.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQSplitterHandle.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStackedLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStackedWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStatusBar.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyle.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleFactory.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturn.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturnMask.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturnVariant.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOption.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionButton.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionComboBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionComplex.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionDockWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionFocusRect.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionFrame.cc | 2 +- .../qt5/QtWidgets/gsiDeclQStyleOptionGraphicsItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionGroupBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionHeader.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionMenuItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionProgressBar.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionRubberBand.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSizeGrip.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSlider.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSpinBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTab.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTabBarBase.cc | 2 +- .../qt5/QtWidgets/gsiDeclQStyleOptionTabWidgetFrame.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTitleBar.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolBar.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolButton.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionViewItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStylePainter.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStylePlugin.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQStyledItemDelegate.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQSwipeGesture.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQSystemTrayIcon.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTabBar.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTabWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTableView.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidgetItem.cc | 2 +- .../qt5/QtWidgets/gsiDeclQTableWidgetSelectionRange.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTapAndHoldGesture.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTapGesture.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTextBrowser.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTextEdit.cc | 2 +- .../qt5/QtWidgets/gsiDeclQTextEdit_ExtraSelection.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTimeEdit.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQToolBar.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQToolBox.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQToolButton.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQToolTip.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTreeView.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidgetItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidgetItemIterator.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQUndoCommand.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQUndoGroup.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQUndoStack.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQUndoView.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQVBoxLayout.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQWhatsThis.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQWidget.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQWidgetAction.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQWidgetItem.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQWizard.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQWizardPage.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQtWidgetsAdd.cc | 2 +- src/gsiqt/qt5/QtWidgets/gsiDeclQtWidgetsTypeTraits.h | 2 +- src/gsiqt/qt5/QtWidgets/gsiQtExternals.h | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomAttr.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomCDATASection.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomCharacterData.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomComment.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomDocument.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomDocumentFragment.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomDocumentType.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomElement.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomEntity.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomEntityReference.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomImplementation.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomNamedNodeMap.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomNode.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomNodeList.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomNotation.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomProcessingInstruction.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQDomText.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlAttributes.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlContentHandler.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlDTDHandler.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlDeclHandler.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlDefaultHandler.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlEntityResolver.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlErrorHandler.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlInputSource.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlLexicalHandler.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlLocator.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlNamespaceSupport.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlParseException.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlReader.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQXmlSimpleReader.cc | 2 +- src/gsiqt/qt5/QtXml/gsiDeclQtXmlTypeTraits.h | 2 +- src/gsiqt/qt5/QtXml/gsiQtExternals.h | 2 +- .../qt5/QtXmlPatterns/gsiDeclQAbstractMessageHandler.cc | 2 +- .../qt5/QtXmlPatterns/gsiDeclQAbstractUriResolver.cc | 2 +- .../qt5/QtXmlPatterns/gsiDeclQAbstractXmlNodeModel.cc | 2 +- .../qt5/QtXmlPatterns/gsiDeclQAbstractXmlReceiver.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQSimpleXmlNodeModel.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQSourceLocation.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlFormatter.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlItem.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlName.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlNamePool.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlNodeModelIndex.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlQuery.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlResultItems.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSchema.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSchemaValidator.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSerializer.cc | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiDeclQtXmlPatternsAdd.cc | 2 +- .../qt5/QtXmlPatterns/gsiDeclQtXmlPatternsTypeTraits.h | 2 +- src/gsiqt/qt5/QtXmlPatterns/gsiQtExternals.h | 2 +- src/gsiqt/qtbasic/gsiDeclQtAllTypeTraits.h | 2 +- src/gsiqt/qtbasic/gsiQt.cc | 2 +- src/gsiqt/qtbasic/gsiQt.h | 2 +- src/gsiqt/qtbasic/gsiQtBasicCommon.h | 2 +- src/gsiqt/qtbasic/gsiQtCoreExternals.h | 2 +- src/gsiqt/qtbasic/gsiQtDesignerExternals.h | 2 +- src/gsiqt/qtbasic/gsiQtGuiExternals.h | 2 +- src/gsiqt/qtbasic/gsiQtHelper.cc | 2 +- src/gsiqt/qtbasic/gsiQtHelper.h | 2 +- src/gsiqt/qtbasic/gsiQtMultimediaExternals.h | 2 +- src/gsiqt/qtbasic/gsiQtNetworkExternals.h | 2 +- src/gsiqt/qtbasic/gsiQtPrintSupportExternals.h | 2 +- src/gsiqt/qtbasic/gsiQtSqlExternals.h | 2 +- src/gsiqt/qtbasic/gsiQtSvgExternals.h | 2 +- src/gsiqt/qtbasic/gsiQtWidgetsExternals.h | 2 +- src/gsiqt/qtbasic/gsiQtXmlExternals.h | 2 +- src/gsiqt/qtbasic/gsiQtXmlPatternsExternals.h | 2 +- src/gtfui/gtfUiDialog.cc | 2 +- src/gtfui/gtfUiDialog.h | 2 +- src/gtfui/gtfui.cc | 2 +- src/img/img/gsiDeclImg.cc | 2 +- src/img/img/imgCommon.h | 2 +- src/img/img/imgForceLink.cc | 2 +- src/img/img/imgForceLink.h | 2 +- src/img/img/imgLandmarksDialog.cc | 2 +- src/img/img/imgLandmarksDialog.h | 2 +- src/img/img/imgNavigator.cc | 2 +- src/img/img/imgNavigator.h | 2 +- src/img/img/imgObject.cc | 2 +- src/img/img/imgObject.h | 2 +- src/img/img/imgPlugin.cc | 2 +- src/img/img/imgPlugin.h | 2 +- src/img/img/imgPropertiesPage.cc | 2 +- src/img/img/imgPropertiesPage.h | 2 +- src/img/img/imgService.cc | 2 +- src/img/img/imgService.h | 2 +- src/img/img/imgWidgets.cc | 2 +- src/img/img/imgWidgets.h | 2 +- src/img/unit_tests/imgObject.cc | 2 +- src/klayout_main/klayout_main/klayout.cc | 2 +- src/klayout_main/tests/klayout_main_tests.cc | 2 +- src/lay/lay/gsiDeclLayApplication.cc | 2 +- src/lay/lay/gsiDeclLayHelpDialog.cc | 2 +- src/lay/lay/gsiDeclLayMainWindow.cc | 2 +- src/lay/lay/layApplication.cc | 2 +- src/lay/lay/layApplication.h | 2 +- src/lay/lay/layClipDialog.cc | 2 +- src/lay/lay/layClipDialog.h | 2 +- src/lay/lay/layCommon.h | 2 +- src/lay/lay/layConfig.h | 2 +- src/lay/lay/layCrashMessage.cc | 2 +- src/lay/lay/layCrashMessage.h | 2 +- src/lay/lay/layFillDialog.cc | 2 +- src/lay/lay/layFillDialog.h | 2 +- src/lay/lay/layFontController.cc | 2 +- src/lay/lay/layFontController.h | 2 +- src/lay/lay/layForceLink.cc | 2 +- src/lay/lay/layForceLink.h | 2 +- src/lay/lay/layGSIHelpProvider.cc | 2 +- src/lay/lay/layGSIHelpProvider.h | 2 +- src/lay/lay/layGenericSyntaxHighlighter.cc | 2 +- src/lay/lay/layGenericSyntaxHighlighter.h | 2 +- src/lay/lay/layHelpDialog.cc | 2 +- src/lay/lay/layHelpDialog.h | 2 +- src/lay/lay/layHelpProvider.cc | 2 +- src/lay/lay/layHelpProvider.h | 2 +- src/lay/lay/layHelpSource.cc | 2 +- src/lay/lay/layHelpSource.h | 2 +- src/lay/lay/layInit.cc | 2 +- src/lay/lay/layInit.h | 2 +- src/lay/lay/layLayoutStatisticsForm.cc | 2 +- src/lay/lay/layLayoutStatisticsForm.h | 2 +- src/lay/lay/layLibraryController.cc | 2 +- src/lay/lay/layLibraryController.h | 2 +- src/lay/lay/layLogViewerDialog.cc | 2 +- src/lay/lay/layLogViewerDialog.h | 2 +- src/lay/lay/layMacroController.cc | 2 +- src/lay/lay/layMacroController.h | 2 +- src/lay/lay/layMacroEditorDialog.cc | 2 +- src/lay/lay/layMacroEditorDialog.h | 2 +- src/lay/lay/layMacroEditorPage.cc | 2 +- src/lay/lay/layMacroEditorPage.h | 2 +- src/lay/lay/layMacroEditorSetupPage.cc | 2 +- src/lay/lay/layMacroEditorSetupPage.h | 2 +- src/lay/lay/layMacroEditorTree.cc | 2 +- src/lay/lay/layMacroEditorTree.h | 2 +- src/lay/lay/layMacroPropertiesDialog.cc | 2 +- src/lay/lay/layMacroPropertiesDialog.h | 2 +- src/lay/lay/layMacroVariableView.cc | 2 +- src/lay/lay/layMacroVariableView.h | 2 +- src/lay/lay/layMainConfigPages.cc | 2 +- src/lay/lay/layMainConfigPages.h | 2 +- src/lay/lay/layMainWindow.cc | 2 +- src/lay/lay/layMainWindow.h | 2 +- src/lay/lay/layNativePlugin.cc | 2 +- src/lay/lay/layNativePlugin.h | 2 +- src/lay/lay/layNavigator.cc | 2 +- src/lay/lay/layNavigator.h | 2 +- src/lay/lay/layPasswordDialog.cc | 2 +- src/lay/lay/layPasswordDialog.h | 2 +- src/lay/lay/layProgress.cc | 2 +- src/lay/lay/layProgress.h | 2 +- src/lay/lay/layProgressWidget.cc | 2 +- src/lay/lay/layProgressWidget.h | 2 +- src/lay/lay/layResourceHelpProvider.cc | 2 +- src/lay/lay/layResourceHelpProvider.h | 2 +- src/lay/lay/layRuntimeErrorForm.cc | 2 +- src/lay/lay/layRuntimeErrorForm.h | 2 +- src/lay/lay/laySalt.cc | 2 +- src/lay/lay/laySalt.h | 2 +- src/lay/lay/laySaltController.cc | 2 +- src/lay/lay/laySaltController.h | 2 +- src/lay/lay/laySaltDownloadManager.cc | 2 +- src/lay/lay/laySaltDownloadManager.h | 2 +- src/lay/lay/laySaltGrain.cc | 2 +- src/lay/lay/laySaltGrain.h | 2 +- src/lay/lay/laySaltGrainDetailsTextWidget.cc | 2 +- src/lay/lay/laySaltGrainDetailsTextWidget.h | 2 +- src/lay/lay/laySaltGrainPropertiesDialog.cc | 2 +- src/lay/lay/laySaltGrainPropertiesDialog.h | 2 +- src/lay/lay/laySaltGrains.cc | 2 +- src/lay/lay/laySaltGrains.h | 2 +- src/lay/lay/laySaltManagerDialog.cc | 2 +- src/lay/lay/laySaltManagerDialog.h | 2 +- src/lay/lay/laySaltModel.cc | 2 +- src/lay/lay/laySaltModel.h | 2 +- src/lay/lay/laySearchReplaceConfigPage.cc | 2 +- src/lay/lay/laySearchReplaceConfigPage.h | 2 +- src/lay/lay/laySearchReplaceDialog.cc | 2 +- src/lay/lay/laySearchReplaceDialog.h | 2 +- src/lay/lay/laySearchReplacePlugin.cc | 2 +- src/lay/lay/laySearchReplacePropertiesWidgets.cc | 2 +- src/lay/lay/laySearchReplacePropertiesWidgets.h | 2 +- src/lay/lay/laySelectCellViewForm.cc | 2 +- src/lay/lay/laySelectCellViewForm.h | 2 +- src/lay/lay/laySession.cc | 2 +- src/lay/lay/laySession.h | 2 +- src/lay/lay/laySettingsForm.cc | 2 +- src/lay/lay/laySettingsForm.h | 2 +- src/lay/lay/laySignalHandler.cc | 2 +- src/lay/lay/laySignalHandler.h | 2 +- src/lay/lay/laySystemPaths.cc | 2 +- src/lay/lay/laySystemPaths.h | 2 +- src/lay/lay/layTechSetupDialog.cc | 2 +- src/lay/lay/layTechSetupDialog.h | 2 +- src/lay/lay/layTechnologyController.cc | 2 +- src/lay/lay/layTechnologyController.h | 2 +- src/lay/lay/layTextProgress.cc | 2 +- src/lay/lay/layTextProgress.h | 2 +- src/lay/lay/layVersion.cc | 2 +- src/lay/lay/layVersion.h | 2 +- src/lay/unit_tests/laySalt.cc | 2 +- src/laybasic/laybasic/gsiDeclLayDialogs.cc | 2 +- src/laybasic/laybasic/gsiDeclLayLayers.cc | 2 +- src/laybasic/laybasic/gsiDeclLayLayoutView.cc | 2 +- src/laybasic/laybasic/gsiDeclLayMarker.cc | 2 +- src/laybasic/laybasic/gsiDeclLayMenu.cc | 2 +- src/laybasic/laybasic/gsiDeclLayPlugin.cc | 2 +- src/laybasic/laybasic/gsiDeclLayStream.cc | 2 +- src/laybasic/laybasic/gtf.cc | 2 +- src/laybasic/laybasic/gtf.h | 2 +- src/laybasic/laybasic/gtfdummy.cc | 2 +- src/laybasic/laybasic/layAbstractMenu.cc | 2 +- src/laybasic/laybasic/layAbstractMenu.h | 2 +- src/laybasic/laybasic/layAbstractMenuProvider.cc | 2 +- src/laybasic/laybasic/layAbstractMenuProvider.h | 2 +- src/laybasic/laybasic/layAnnotationShapes.cc | 2 +- src/laybasic/laybasic/layAnnotationShapes.h | 2 +- src/laybasic/laybasic/layBackgroundAwareTreeStyle.cc | 2 +- src/laybasic/laybasic/layBackgroundAwareTreeStyle.h | 2 +- src/laybasic/laybasic/layBitmap.cc | 2 +- src/laybasic/laybasic/layBitmap.h | 2 +- src/laybasic/laybasic/layBitmapRenderer.cc | 2 +- src/laybasic/laybasic/layBitmapRenderer.h | 2 +- src/laybasic/laybasic/layBitmapsToImage.cc | 2 +- src/laybasic/laybasic/layBitmapsToImage.h | 2 +- src/laybasic/laybasic/layBookmarkList.cc | 2 +- src/laybasic/laybasic/layBookmarkList.h | 2 +- src/laybasic/laybasic/layBookmarkManagementForm.cc | 2 +- src/laybasic/laybasic/layBookmarkManagementForm.h | 2 +- src/laybasic/laybasic/layBrowseInstancesForm.cc | 2 +- src/laybasic/laybasic/layBrowseInstancesForm.h | 2 +- src/laybasic/laybasic/layBrowseShapesForm.cc | 2 +- src/laybasic/laybasic/layBrowseShapesForm.h | 2 +- src/laybasic/laybasic/layBrowser.cc | 2 +- src/laybasic/laybasic/layBrowser.h | 2 +- src/laybasic/laybasic/layBrowserDialog.cc | 2 +- src/laybasic/laybasic/layBrowserDialog.h | 2 +- src/laybasic/laybasic/layBrowserPanel.cc | 2 +- src/laybasic/laybasic/layBrowserPanel.h | 2 +- src/laybasic/laybasic/layCanvasPlane.cc | 2 +- src/laybasic/laybasic/layCanvasPlane.h | 2 +- src/laybasic/laybasic/layCellSelectionForm.cc | 2 +- src/laybasic/laybasic/layCellSelectionForm.h | 2 +- src/laybasic/laybasic/layCellTreeModel.cc | 2 +- src/laybasic/laybasic/layCellTreeModel.h | 2 +- src/laybasic/laybasic/layCellView.cc | 2 +- src/laybasic/laybasic/layCellView.h | 2 +- src/laybasic/laybasic/layColorPalette.cc | 2 +- src/laybasic/laybasic/layColorPalette.h | 2 +- src/laybasic/laybasic/layConfigurationDialog.cc | 2 +- src/laybasic/laybasic/layConfigurationDialog.h | 2 +- src/laybasic/laybasic/layConverters.cc | 2 +- src/laybasic/laybasic/layConverters.h | 2 +- src/laybasic/laybasic/layCursor.cc | 2 +- src/laybasic/laybasic/layCursor.h | 2 +- src/laybasic/laybasic/layDialogs.cc | 2 +- src/laybasic/laybasic/layDialogs.h | 2 +- src/laybasic/laybasic/layDisplayState.cc | 2 +- src/laybasic/laybasic/layDisplayState.h | 2 +- src/laybasic/laybasic/layDitherPattern.cc | 2 +- src/laybasic/laybasic/layDitherPattern.h | 2 +- src/laybasic/laybasic/layDrawing.cc | 2 +- src/laybasic/laybasic/layDrawing.h | 2 +- src/laybasic/laybasic/layEditLineStyleWidget.cc | 2 +- src/laybasic/laybasic/layEditLineStyleWidget.h | 2 +- src/laybasic/laybasic/layEditLineStylesForm.cc | 2 +- src/laybasic/laybasic/layEditLineStylesForm.h | 2 +- src/laybasic/laybasic/layEditStippleWidget.cc | 2 +- src/laybasic/laybasic/layEditStippleWidget.h | 2 +- src/laybasic/laybasic/layEditStipplesForm.cc | 2 +- src/laybasic/laybasic/layEditStipplesForm.h | 2 +- src/laybasic/laybasic/layEditable.cc | 2 +- src/laybasic/laybasic/layEditable.h | 2 +- src/laybasic/laybasic/layFileDialog.cc | 2 +- src/laybasic/laybasic/layFileDialog.h | 2 +- src/laybasic/laybasic/layFinder.cc | 2 +- src/laybasic/laybasic/layFinder.h | 2 +- src/laybasic/laybasic/layFixedFont.cc | 2 +- src/laybasic/laybasic/layGridNet.cc | 2 +- src/laybasic/laybasic/layGridNet.h | 2 +- src/laybasic/laybasic/layHierarchyControlPanel.cc | 2 +- src/laybasic/laybasic/layHierarchyControlPanel.h | 2 +- src/laybasic/laybasic/layLayerControlPanel.cc | 2 +- src/laybasic/laybasic/layLayerControlPanel.h | 2 +- src/laybasic/laybasic/layLayerMappingWidget.cc | 2 +- src/laybasic/laybasic/layLayerMappingWidget.h | 2 +- src/laybasic/laybasic/layLayerProperties.cc | 2 +- src/laybasic/laybasic/layLayerProperties.h | 2 +- src/laybasic/laybasic/layLayerToolbox.cc | 2 +- src/laybasic/laybasic/layLayerToolbox.h | 2 +- src/laybasic/laybasic/layLayerTreeModel.cc | 2 +- src/laybasic/laybasic/layLayerTreeModel.h | 2 +- src/laybasic/laybasic/layLayoutCanvas.cc | 2 +- src/laybasic/laybasic/layLayoutCanvas.h | 2 +- src/laybasic/laybasic/layLayoutPropertiesForm.cc | 2 +- src/laybasic/laybasic/layLayoutPropertiesForm.h | 2 +- src/laybasic/laybasic/layLayoutView.cc | 2 +- src/laybasic/laybasic/layLayoutView.h | 2 +- src/laybasic/laybasic/layLayoutViewConfigPages.cc | 2 +- src/laybasic/laybasic/layLayoutViewConfigPages.h | 2 +- src/laybasic/laybasic/layLineStylePalette.cc | 2 +- src/laybasic/laybasic/layLineStylePalette.h | 2 +- src/laybasic/laybasic/layLineStyles.cc | 2 +- src/laybasic/laybasic/layLineStyles.h | 2 +- src/laybasic/laybasic/layLoadLayoutOptionsDialog.cc | 2 +- src/laybasic/laybasic/layLoadLayoutOptionsDialog.h | 2 +- src/laybasic/laybasic/layMarker.cc | 2 +- src/laybasic/laybasic/layMarker.h | 2 +- src/laybasic/laybasic/layMouseTracker.cc | 2 +- src/laybasic/laybasic/layMouseTracker.h | 2 +- src/laybasic/laybasic/layMove.cc | 2 +- src/laybasic/laybasic/layMove.h | 2 +- src/laybasic/laybasic/layObjectInstPath.cc | 2 +- src/laybasic/laybasic/layObjectInstPath.h | 2 +- src/laybasic/laybasic/layParsedLayerSource.cc | 2 +- src/laybasic/laybasic/layParsedLayerSource.h | 2 +- src/laybasic/laybasic/layPlugin.cc | 2 +- src/laybasic/laybasic/layPlugin.h | 2 +- src/laybasic/laybasic/layProperties.cc | 2 +- src/laybasic/laybasic/layProperties.h | 2 +- src/laybasic/laybasic/layPropertiesDialog.cc | 2 +- src/laybasic/laybasic/layPropertiesDialog.h | 2 +- src/laybasic/laybasic/layQtTools.cc | 2 +- src/laybasic/laybasic/layQtTools.h | 2 +- src/laybasic/laybasic/layRedrawLayerInfo.cc | 2 +- src/laybasic/laybasic/layRedrawLayerInfo.h | 2 +- src/laybasic/laybasic/layRedrawThread.cc | 2 +- src/laybasic/laybasic/layRedrawThread.h | 2 +- src/laybasic/laybasic/layRedrawThreadCanvas.cc | 2 +- src/laybasic/laybasic/layRedrawThreadCanvas.h | 2 +- src/laybasic/laybasic/layRedrawThreadWorker.cc | 2 +- src/laybasic/laybasic/layRedrawThreadWorker.h | 2 +- src/laybasic/laybasic/layRenderer.cc | 2 +- src/laybasic/laybasic/layRenderer.h | 2 +- src/laybasic/laybasic/layRubberBox.cc | 2 +- src/laybasic/laybasic/layRubberBox.h | 2 +- src/laybasic/laybasic/laySaveLayoutOptionsDialog.cc | 2 +- src/laybasic/laybasic/laySaveLayoutOptionsDialog.h | 2 +- src/laybasic/laybasic/laySelectLineStyleForm.cc | 2 +- src/laybasic/laybasic/laySelectLineStyleForm.h | 2 +- src/laybasic/laybasic/laySelectStippleForm.cc | 2 +- src/laybasic/laybasic/laySelectStippleForm.h | 2 +- src/laybasic/laybasic/laySelector.cc | 2 +- src/laybasic/laybasic/laySelector.h | 2 +- src/laybasic/laybasic/laySnap.cc | 2 +- src/laybasic/laybasic/laySnap.h | 2 +- src/laybasic/laybasic/layStipplePalette.cc | 2 +- src/laybasic/laybasic/layStipplePalette.h | 2 +- src/laybasic/laybasic/layStream.cc | 2 +- src/laybasic/laybasic/layStream.h | 2 +- src/laybasic/laybasic/layTechnology.cc | 2 +- src/laybasic/laybasic/layTechnology.h | 2 +- src/laybasic/laybasic/layTipDialog.cc | 2 +- src/laybasic/laybasic/layTipDialog.h | 2 +- src/laybasic/laybasic/layViewObject.cc | 2 +- src/laybasic/laybasic/layViewObject.h | 2 +- src/laybasic/laybasic/layViewOp.cc | 2 +- src/laybasic/laybasic/layViewOp.h | 2 +- src/laybasic/laybasic/layViewport.cc | 2 +- src/laybasic/laybasic/layViewport.h | 2 +- src/laybasic/laybasic/layWidgets.cc | 2 +- src/laybasic/laybasic/layWidgets.h | 2 +- src/laybasic/laybasic/layZoomBox.cc | 2 +- src/laybasic/laybasic/layZoomBox.h | 2 +- src/laybasic/laybasic/laybasicCommon.h | 2 +- src/laybasic/laybasic/laybasicConfig.h | 2 +- src/laybasic/laybasic/rdbInfoWidget.cc | 2 +- src/laybasic/laybasic/rdbInfoWidget.h | 2 +- src/laybasic/laybasic/rdbMarkerBrowser.cc | 2 +- src/laybasic/laybasic/rdbMarkerBrowser.h | 2 +- src/laybasic/laybasic/rdbMarkerBrowserDialog.cc | 2 +- src/laybasic/laybasic/rdbMarkerBrowserDialog.h | 2 +- src/laybasic/laybasic/rdbMarkerBrowserPage.cc | 2 +- src/laybasic/laybasic/rdbMarkerBrowserPage.h | 2 +- src/laybasic/unit_tests/layAbstractMenu.cc | 2 +- src/laybasic/unit_tests/layAnnotationShapes.cc | 2 +- src/laybasic/unit_tests/layBitmap.cc | 2 +- src/laybasic/unit_tests/layBitmapsToImage.cc | 2 +- src/laybasic/unit_tests/layLayerProperties.cc | 2 +- src/laybasic/unit_tests/layParsedLayerSource.cc | 2 +- src/laybasic/unit_tests/layRenderer.cc | 2 +- src/laybasic/unit_tests/laySnap.cc | 2 +- src/lib/lib/libBasic.cc | 2 +- src/lib/lib/libBasicArc.cc | 2 +- src/lib/lib/libBasicArc.h | 2 +- src/lib/lib/libBasicCircle.cc | 2 +- src/lib/lib/libBasicCircle.h | 2 +- src/lib/lib/libBasicDonut.cc | 2 +- src/lib/lib/libBasicDonut.h | 2 +- src/lib/lib/libBasicEllipse.cc | 2 +- src/lib/lib/libBasicEllipse.h | 2 +- src/lib/lib/libBasicPie.cc | 2 +- src/lib/lib/libBasicPie.h | 2 +- src/lib/lib/libBasicRoundPath.cc | 2 +- src/lib/lib/libBasicRoundPath.h | 2 +- src/lib/lib/libBasicRoundPolygon.cc | 2 +- src/lib/lib/libBasicRoundPolygon.h | 2 +- src/lib/lib/libBasicStrokedPolygon.cc | 2 +- src/lib/lib/libBasicStrokedPolygon.h | 2 +- src/lib/lib/libBasicText.cc | 2 +- src/lib/lib/libBasicText.h | 2 +- src/lib/lib/libCommon.h | 2 +- src/lib/lib/libForceLink.cc | 2 +- src/lib/lib/libForceLink.h | 2 +- src/lib/unit_tests/libBasicTests.cc | 2 +- src/lym/lym/gsiDeclLymMacro.cc | 2 +- src/lym/lym/lymCommon.h | 2 +- src/lym/lym/lymMacro.cc | 2 +- src/lym/lym/lymMacro.h | 2 +- src/lym/lym/lymMacroInterpreter.cc | 2 +- src/lym/lym/lymMacroInterpreter.h | 2 +- src/lym/unit_tests/lymBasicTests.cc | 2 +- src/plugins/common/dbPluginCommon.h | 2 +- src/plugins/common/layPluginCommon.h | 2 +- src/plugins/streamers/cif/db_plugin/dbCIF.cc | 2 +- src/plugins/streamers/cif/db_plugin/dbCIF.h | 2 +- src/plugins/streamers/cif/db_plugin/dbCIFFormat.h | 2 +- src/plugins/streamers/cif/db_plugin/dbCIFReader.cc | 2 +- src/plugins/streamers/cif/db_plugin/dbCIFReader.h | 2 +- src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc | 2 +- src/plugins/streamers/cif/db_plugin/dbCIFWriter.h | 2 +- src/plugins/streamers/cif/db_plugin/gsiDeclDbCIF.cc | 2 +- .../streamers/cif/lay_plugin/layCIFReaderPlugin.cc | 2 +- src/plugins/streamers/cif/lay_plugin/layCIFReaderPlugin.h | 2 +- .../streamers/cif/lay_plugin/layCIFWriterPlugin.cc | 2 +- src/plugins/streamers/cif/lay_plugin/layCIFWriterPlugin.h | 2 +- src/plugins/streamers/cif/unit_tests/dbCIFReader.cc | 2 +- .../streamers/common/lay_plugin/layCommonReaderPlugin.cc | 2 +- .../streamers/common/lay_plugin/layCommonReaderPlugin.h | 2 +- src/plugins/streamers/dxf/db_plugin/dbDXF.cc | 2 +- src/plugins/streamers/dxf/db_plugin/dbDXF.h | 2 +- src/plugins/streamers/dxf/db_plugin/dbDXFFormat.h | 2 +- src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc | 2 +- src/plugins/streamers/dxf/db_plugin/dbDXFReader.h | 2 +- src/plugins/streamers/dxf/db_plugin/dbDXFWriter.cc | 2 +- src/plugins/streamers/dxf/db_plugin/dbDXFWriter.h | 2 +- src/plugins/streamers/dxf/db_plugin/gsiDeclDbDXF.cc | 2 +- .../streamers/dxf/lay_plugin/layDXFReaderPlugin.cc | 2 +- src/plugins/streamers/dxf/lay_plugin/layDXFReaderPlugin.h | 2 +- .../streamers/dxf/lay_plugin/layDXFWriterPlugin.cc | 2 +- src/plugins/streamers/dxf/lay_plugin/layDXFWriterPlugin.h | 2 +- src/plugins/streamers/dxf/unit_tests/dbDXFReader.cc | 2 +- .../streamers/gds2/db_plugin/contrib/dbGDS2Converter.cc | 2 +- .../streamers/gds2/db_plugin/contrib/dbGDS2Converter.h | 2 +- .../streamers/gds2/db_plugin/contrib/dbGDS2Text.cc | 2 +- .../streamers/gds2/db_plugin/contrib/dbGDS2TextReader.cc | 2 +- .../streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h | 2 +- .../streamers/gds2/db_plugin/contrib/dbGDS2TextWriter.cc | 2 +- .../streamers/gds2/db_plugin/contrib/dbGDS2TextWriter.h | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2.cc | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2.h | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2Writer.cc | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2Writer.h | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc | 2 +- src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h | 2 +- src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc | 2 +- .../streamers/gds2/lay_plugin/layGDS2ReaderPlugin.cc | 2 +- .../streamers/gds2/lay_plugin/layGDS2ReaderPlugin.h | 2 +- .../streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc | 2 +- .../streamers/gds2/lay_plugin/layGDS2WriterPlugin.h | 2 +- src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc | 2 +- src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc | 2 +- src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc | 2 +- src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h | 2 +- .../streamers/lefdef/db_plugin/dbLEFDEFImporter.cc | 2 +- src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h | 2 +- src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc | 2 +- src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc | 2 +- src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h | 2 +- src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc | 2 +- .../streamers/lefdef/lay_plugin/layLEFDEFImport.cc | 2 +- .../streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc | 2 +- .../streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.h | 2 +- .../streamers/lefdef/lay_plugin/layLEFDEFPlugin.cc | 2 +- src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImport.cc | 2 +- src/plugins/streamers/oasis/db_plugin/dbOASIS.cc | 2 +- src/plugins/streamers/oasis/db_plugin/dbOASIS.h | 2 +- src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h | 2 +- src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc | 2 +- src/plugins/streamers/oasis/db_plugin/dbOASISReader.h | 2 +- src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc | 2 +- src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h | 2 +- src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc | 2 +- .../streamers/oasis/lay_plugin/layOASISReaderPlugin.cc | 2 +- .../streamers/oasis/lay_plugin/layOASISReaderPlugin.h | 2 +- .../streamers/oasis/lay_plugin/layOASISWriterPlugin.cc | 2 +- .../streamers/oasis/lay_plugin/layOASISWriterPlugin.h | 2 +- src/plugins/streamers/oasis/unit_tests/dbOASISReader.cc | 2 +- src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc | 2 +- src/plugins/streamers/oasis/unit_tests/dbOASISWriter2.cc | 2 +- .../streamers/pcb/db_plugin/dbGerberDrillFileReader.cc | 2 +- .../streamers/pcb/db_plugin/dbGerberDrillFileReader.h | 2 +- src/plugins/streamers/pcb/db_plugin/dbGerberImportData.cc | 2 +- src/plugins/streamers/pcb/db_plugin/dbGerberImportData.h | 2 +- src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc | 2 +- src/plugins/streamers/pcb/db_plugin/dbGerberImporter.h | 2 +- src/plugins/streamers/pcb/db_plugin/dbRS274XApertures.cc | 2 +- src/plugins/streamers/pcb/db_plugin/dbRS274XApertures.h | 2 +- src/plugins/streamers/pcb/db_plugin/dbRS274XReader.cc | 2 +- src/plugins/streamers/pcb/db_plugin/dbRS274XReader.h | 2 +- src/plugins/streamers/pcb/lay_plugin/layGerberImport.cc | 2 +- .../streamers/pcb/lay_plugin/layGerberImportDialog.cc | 2 +- .../streamers/pcb/lay_plugin/layGerberImportDialog.h | 2 +- src/plugins/streamers/pcb/unit_tests/dbGerberImport.cc | 2 +- .../tools/bool/lay_plugin/layBooleanOperationsDialogs.cc | 2 +- .../tools/bool/lay_plugin/layBooleanOperationsDialogs.h | 2 +- .../tools/bool/lay_plugin/layBooleanOperationsPlugin.cc | 2 +- src/plugins/tools/diff/lay_plugin/layDiffPlugin.cc | 2 +- src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc | 2 +- src/plugins/tools/diff/lay_plugin/layDiffToolDialog.h | 2 +- src/plugins/tools/import/lay_plugin/layStreamImport.cc | 2 +- .../tools/import/lay_plugin/layStreamImportDialog.cc | 2 +- .../tools/import/lay_plugin/layStreamImportDialog.h | 2 +- src/plugins/tools/import/lay_plugin/layStreamImporter.cc | 2 +- src/plugins/tools/import/lay_plugin/layStreamImporter.h | 2 +- src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc | 2 +- src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h | 2 +- src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc | 2 +- src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h | 2 +- .../tools/net_tracer/db_plugin/dbNetTracerPlugin.cc | 2 +- .../tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc | 2 +- .../tools/net_tracer/lay_plugin/layNetTracerConfig.cc | 2 +- .../tools/net_tracer/lay_plugin/layNetTracerConfig.h | 2 +- .../tools/net_tracer/lay_plugin/layNetTracerDialog.cc | 2 +- .../tools/net_tracer/lay_plugin/layNetTracerDialog.h | 2 +- src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.cc | 2 +- src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.h | 2 +- .../tools/net_tracer/lay_plugin/layNetTracerPlugin.cc | 2 +- src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc | 2 +- src/plugins/tools/xor/lay_plugin/layXORPlugin.cc | 2 +- src/plugins/tools/xor/lay_plugin/layXORProgress.cc | 2 +- src/plugins/tools/xor/lay_plugin/layXORProgress.h | 2 +- src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc | 2 +- src/plugins/tools/xor/lay_plugin/layXORToolDialog.h | 2 +- src/pya/pya/pya.cc | 2 +- src/pya/pya/pya.h | 2 +- src/pya/pya/pyaCommon.h | 2 +- src/pya/pya/pyaConvert.cc | 2 +- src/pya/pya/pyaConvert.h | 2 +- src/pya/pya/pyaHelpers.cc | 2 +- src/pya/pya/pyaHelpers.h | 2 +- src/pya/pya/pyaInspector.cc | 2 +- src/pya/pya/pyaInspector.h | 2 +- src/pya/pya/pyaMarshal.cc | 2 +- src/pya/pya/pyaMarshal.h | 2 +- src/pya/pya/pyaModule.cc | 2 +- src/pya/pya/pyaModule.h | 2 +- src/pya/pya/pyaObject.cc | 2 +- src/pya/pya/pyaObject.h | 2 +- src/pya/pya/pyaRefs.cc | 2 +- src/pya/pya/pyaRefs.h | 2 +- src/pya/pya/pyaSignalHandler.cc | 2 +- src/pya/pya/pyaSignalHandler.h | 2 +- src/pya/pya/pyaStatusChangedListener.cc | 2 +- src/pya/pya/pyaStatusChangedListener.h | 2 +- src/pya/pya/pyaUtils.cc | 2 +- src/pya/pya/pyaUtils.h | 2 +- src/pya/unit_tests/pya.cc | 2 +- src/pyastub/pya.cc | 2 +- src/pyastub/pya.h | 2 +- src/pyastub/pyaCommon.h | 2 +- src/pymod/QtCore/QtCoreMain.cc | 2 +- src/pymod/QtDesigner/QtDesignerMain.cc | 2 +- src/pymod/QtGui/QtGuiMain.cc | 2 +- src/pymod/QtMultimedia/QtMultimediaMain.cc | 2 +- src/pymod/QtNetwork/QtNetworkMain.cc | 2 +- src/pymod/QtPrintSupport/QtPrintSupportMain.cc | 2 +- src/pymod/QtSql/QtSqlMain.cc | 2 +- src/pymod/QtSvg/QtSvgMain.cc | 2 +- src/pymod/QtWidgets/QtWidgetsMain.cc | 2 +- src/pymod/QtXml/QtXmlMain.cc | 2 +- src/pymod/QtXmlPatterns/QtXmlPatternsMain.cc | 2 +- src/pymod/bridge_sample/bridge_sample.cc | 2 +- src/pymod/db/dbMain.cc | 2 +- src/pymod/lay/layMain.cc | 2 +- src/pymod/pymodHelper.h | 2 +- src/pymod/rdb/rdbMain.cc | 2 +- src/pymod/tl/tlMain.cc | 2 +- src/pymod/unit_tests/pymod_tests.cc | 2 +- src/rba/rba/rba.cc | 2 +- src/rba/rba/rba.h | 2 +- src/rba/rba/rbaCommon.h | 2 +- src/rba/rba/rbaConvert.cc | 2 +- src/rba/rba/rbaConvert.h | 2 +- src/rba/rba/rbaInspector.cc | 2 +- src/rba/rba/rbaInspector.h | 2 +- src/rba/rba/rbaInternal.cc | 2 +- src/rba/rba/rbaInternal.h | 2 +- src/rba/rba/rbaMarshal.cc | 2 +- src/rba/rba/rbaMarshal.h | 2 +- src/rba/rba/rbaUtils.cc | 2 +- src/rba/rba/rbaUtils.h | 2 +- src/rba/unit_tests/rba.cc | 2 +- src/rbastub/rba.cc | 2 +- src/rbastub/rba.h | 2 +- src/rbastub/rbaCommon.h | 2 +- src/rdb/rdb/gsiDeclRdb.cc | 2 +- src/rdb/rdb/rdb.cc | 2 +- src/rdb/rdb/rdb.h | 2 +- src/rdb/rdb/rdbCommon.h | 2 +- src/rdb/rdb/rdbFile.cc | 2 +- src/rdb/rdb/rdbForceLink.cc | 2 +- src/rdb/rdb/rdbForceLink.h | 2 +- src/rdb/rdb/rdbRVEReader.cc | 2 +- src/rdb/rdb/rdbReader.cc | 2 +- src/rdb/rdb/rdbReader.h | 2 +- src/rdb/rdb/rdbTiledRdbOutputReceiver.cc | 2 +- src/rdb/rdb/rdbTiledRdbOutputReceiver.h | 2 +- src/rdb/rdb/rdbUtils.cc | 2 +- src/rdb/rdb/rdbUtils.h | 2 +- src/rdb/unit_tests/rdb.cc | 2 +- src/tl/tl/tlArch.cc | 2 +- src/tl/tl/tlArch.h | 2 +- src/tl/tl/tlAssert.cc | 2 +- src/tl/tl/tlAssert.h | 2 +- src/tl/tl/tlClassRegistry.cc | 2 +- src/tl/tl/tlClassRegistry.h | 2 +- src/tl/tl/tlCommandLineParser.cc | 2 +- src/tl/tl/tlCommandLineParser.h | 2 +- src/tl/tl/tlCommon.h | 2 +- src/tl/tl/tlCpp.h | 2 +- src/tl/tl/tlDataMapping.cc | 2 +- src/tl/tl/tlDataMapping.h | 2 +- src/tl/tl/tlDeferredExecution.cc | 2 +- src/tl/tl/tlDeferredExecution.h | 2 +- src/tl/tl/tlDeferredExecutionQt.cc | 2 +- src/tl/tl/tlDeferredExecutionQt.h | 2 +- src/tl/tl/tlDeflate.cc | 2 +- src/tl/tl/tlDeflate.h | 2 +- src/tl/tl/tlDefs.h | 2 +- src/tl/tl/tlEvents.cc | 2 +- src/tl/tl/tlEvents.h | 2 +- src/tl/tl/tlEventsVar.h | 2 +- src/tl/tl/tlException.cc | 2 +- src/tl/tl/tlException.h | 2 +- src/tl/tl/tlExceptions.cc | 2 +- src/tl/tl/tlExceptions.h | 2 +- src/tl/tl/tlExpression.cc | 2 +- src/tl/tl/tlExpression.h | 2 +- src/tl/tl/tlFileSystemWatcher.cc | 2 +- src/tl/tl/tlFileSystemWatcher.h | 2 +- src/tl/tl/tlFileUtils.cc | 2 +- src/tl/tl/tlFileUtils.h | 2 +- src/tl/tl/tlFixedVector.h | 2 +- src/tl/tl/tlGlobPattern.cc | 2 +- src/tl/tl/tlGlobPattern.h | 2 +- src/tl/tl/tlHeap.cc | 2 +- src/tl/tl/tlHeap.h | 2 +- src/tl/tl/tlHttpStream.cc | 2 +- src/tl/tl/tlHttpStream.h | 2 +- src/tl/tl/tlHttpStreamCurl.cc | 2 +- src/tl/tl/tlHttpStreamCurl.h | 2 +- src/tl/tl/tlHttpStreamNoQt.cc | 2 +- src/tl/tl/tlHttpStreamQt.cc | 2 +- src/tl/tl/tlHttpStreamQt.h | 2 +- src/tl/tl/tlInt128Support.cc | 2 +- src/tl/tl/tlInt128Support.h | 2 +- src/tl/tl/tlInternational.cc | 2 +- src/tl/tl/tlInternational.h | 2 +- src/tl/tl/tlIntervalMap.h | 2 +- src/tl/tl/tlIntervalSet.h | 2 +- src/tl/tl/tlKDTree.h | 2 +- src/tl/tl/tlList.cc | 2 +- src/tl/tl/tlList.h | 2 +- src/tl/tl/tlLog.cc | 2 +- src/tl/tl/tlLog.h | 2 +- src/tl/tl/tlLongInt.cc | 2 +- src/tl/tl/tlLongInt.h | 2 +- src/tl/tl/tlMath.h | 2 +- src/tl/tl/tlObject.cc | 2 +- src/tl/tl/tlObject.h | 2 +- src/tl/tl/tlObjectCollection.h | 2 +- src/tl/tl/tlProgress.cc | 2 +- src/tl/tl/tlProgress.h | 2 +- src/tl/tl/tlReuseVector.h | 2 +- src/tl/tl/tlScriptError.cc | 2 +- src/tl/tl/tlScriptError.h | 2 +- src/tl/tl/tlStableVector.h | 2 +- src/tl/tl/tlStaticObjects.cc | 2 +- src/tl/tl/tlStaticObjects.h | 2 +- src/tl/tl/tlStream.cc | 2 +- src/tl/tl/tlStream.h | 2 +- src/tl/tl/tlString.cc | 2 +- src/tl/tl/tlString.h | 2 +- src/tl/tl/tlThreadedWorkers.cc | 2 +- src/tl/tl/tlThreadedWorkers.h | 2 +- src/tl/tl/tlThreads.cc | 2 +- src/tl/tl/tlThreads.h | 2 +- src/tl/tl/tlTimer.cc | 2 +- src/tl/tl/tlTimer.h | 2 +- src/tl/tl/tlTypeTraits.h | 2 +- src/tl/tl/tlUniqueId.cc | 2 +- src/tl/tl/tlUniqueId.h | 2 +- src/tl/tl/tlUnitTest.cc | 2 +- src/tl/tl/tlUnitTest.h | 2 +- src/tl/tl/tlUri.cc | 2 +- src/tl/tl/tlUri.h | 2 +- src/tl/tl/tlUtils.h | 2 +- src/tl/tl/tlVariant.cc | 2 +- src/tl/tl/tlVariant.h | 2 +- src/tl/tl/tlVariantUserClasses.h | 2 +- src/tl/tl/tlVector.h | 2 +- src/tl/tl/tlWebDAV.cc | 2 +- src/tl/tl/tlWebDAV.h | 2 +- src/tl/tl/tlXMLParser.cc | 2 +- src/tl/tl/tlXMLParser.h | 2 +- src/tl/tl/tlXMLWriter.cc | 2 +- src/tl/tl/tlXMLWriter.h | 2 +- src/tl/unit_tests/tlAlgorithm.cc | 2 +- src/tl/unit_tests/tlClassRegistry.cc | 2 +- src/tl/unit_tests/tlCommandLineParser.cc | 2 +- src/tl/unit_tests/tlDataMapping.cc | 2 +- src/tl/unit_tests/tlDeferredExecution.cc | 2 +- src/tl/unit_tests/tlDeflate.cc | 2 +- src/tl/unit_tests/tlEvents.cc | 2 +- src/tl/unit_tests/tlExpression.cc | 2 +- src/tl/unit_tests/tlFileSystemWatcher.cc | 2 +- src/tl/unit_tests/tlFileUtils.cc | 2 +- src/tl/unit_tests/tlGlobPattern.cc | 2 +- src/tl/unit_tests/tlHttpStream.cc | 2 +- src/tl/unit_tests/tlInt128Support.cc | 2 +- src/tl/unit_tests/tlIntervalMap.cc | 2 +- src/tl/unit_tests/tlIntervalSet.cc | 2 +- src/tl/unit_tests/tlKDTree.cc | 2 +- src/tl/unit_tests/tlListTests.cc | 2 +- src/tl/unit_tests/tlLongInt.cc | 2 +- src/tl/unit_tests/tlMath.cc | 2 +- src/tl/unit_tests/tlObject.cc | 2 +- src/tl/unit_tests/tlReuseVector.cc | 2 +- src/tl/unit_tests/tlStableVector.cc | 2 +- src/tl/unit_tests/tlStreamTests.cc | 2 +- src/tl/unit_tests/tlString.cc | 2 +- src/tl/unit_tests/tlThreadedWorkers.cc | 2 +- src/tl/unit_tests/tlThreads.cc | 2 +- src/tl/unit_tests/tlUniqueIdTests.cc | 2 +- src/tl/unit_tests/tlUri.cc | 2 +- src/tl/unit_tests/tlUtils.cc | 2 +- src/tl/unit_tests/tlVariant.cc | 2 +- src/tl/unit_tests/tlWebDAV.cc | 2 +- src/tl/unit_tests/tlXMLParser.cc | 2 +- src/unit_tests/unit_test_main.cc | 2 +- src/unit_tests/utTestConsole.cc | 2 +- src/unit_tests/utTestConsole.h | 2 +- src/version/version.h | 4 ++-- testdata/klayout_main/main.rb | 2 +- testdata/pymod/bridge.py | 2 +- testdata/pymod/import_QtCore.py | 2 +- testdata/pymod/import_QtDesigner.py | 2 +- testdata/pymod/import_QtGui.py | 2 +- testdata/pymod/import_QtMultimedia.py | 2 +- testdata/pymod/import_QtNetwork.py | 2 +- testdata/pymod/import_QtPrintSupport.py | 2 +- testdata/pymod/import_QtSql.py | 2 +- testdata/pymod/import_QtSvg.py | 2 +- testdata/pymod/import_QtWidgets.py | 2 +- testdata/pymod/import_QtXml.py | 2 +- testdata/pymod/import_QtXmlPatterns.py | 2 +- testdata/pymod/import_db.py | 2 +- testdata/pymod/import_lay.py | 2 +- testdata/pymod/import_lay_noqt.py | 2 +- testdata/pymod/import_rdb.py | 2 +- testdata/pymod/import_tl.py | 2 +- testdata/python/basic.py | 2 +- testdata/python/dbLayoutTest.py | 2 +- testdata/python/dbPCells.py | 2 +- testdata/python/dbPolygonTest.py | 2 +- testdata/python/dbReaders.py | 2 +- testdata/python/dbRegionTest.py | 2 +- testdata/python/dbTransTest.py | 2 +- testdata/python/qtbinding.py | 2 +- testdata/python/tlTest.py | 2 +- testdata/ruby/antTest.rb | 2 +- testdata/ruby/basic.rb | 2 +- testdata/ruby/dbBooleanTest.rb | 2 +- testdata/ruby/dbBoxTest.rb | 2 +- testdata/ruby/dbCellInstArrayTest.rb | 2 +- testdata/ruby/dbCellMapping.rb | 2 +- testdata/ruby/dbEdgePairTest.rb | 2 +- testdata/ruby/dbEdgePairsTest.rb | 2 +- testdata/ruby/dbEdgeTest.rb | 2 +- testdata/ruby/dbEdgesTest.rb | 2 +- testdata/ruby/dbGlyphs.rb | 2 +- testdata/ruby/dbInstElementTest.rb | 2 +- testdata/ruby/dbInstanceTest.rb | 2 +- testdata/ruby/dbLayerMapping.rb | 2 +- testdata/ruby/dbLayout.rb | 2 +- testdata/ruby/dbLayoutDiff.rb | 2 +- testdata/ruby/dbLayoutQuery.rb | 2 +- testdata/ruby/dbLayoutTest.rb | 2 +- testdata/ruby/dbLayoutToNetlist.rb | 2 +- testdata/ruby/dbMatrix.rb | 2 +- testdata/ruby/dbNetlist.rb | 2 +- testdata/ruby/dbNetlistDeviceClasses.rb | 2 +- testdata/ruby/dbPCells.rb | 2 +- testdata/ruby/dbPathTest.rb | 2 +- testdata/ruby/dbPointTest.rb | 2 +- testdata/ruby/dbPolygonTest.rb | 2 +- testdata/ruby/dbReaders.rb | 2 +- testdata/ruby/dbRegionTest.rb | 2 +- testdata/ruby/dbShapesTest.rb | 2 +- testdata/ruby/dbSimplePolygonTest.rb | 2 +- testdata/ruby/dbTextTest.rb | 2 +- testdata/ruby/dbTilingProcessorTest.rb | 2 +- testdata/ruby/dbTransTest.rb | 2 +- testdata/ruby/dbVectorTest.rb | 2 +- testdata/ruby/edtTest.rb | 2 +- testdata/ruby/extNetTracer.rb | 2 +- testdata/ruby/imgObject.rb | 2 +- testdata/ruby/layLayers.rb | 2 +- testdata/ruby/layLayoutView.rb | 2 +- testdata/ruby/layMarkers.rb | 2 +- testdata/ruby/layMenuTest.rb | 2 +- testdata/ruby/laySaveLayoutOptions.rb | 2 +- testdata/ruby/laySession.rb | 2 +- testdata/ruby/layTechnologies.rb | 2 +- testdata/ruby/qtbinding.rb | 2 +- testdata/ruby/rdbTest.rb | 2 +- testdata/ruby/tlTest.rb | 2 +- 2606 files changed, 2610 insertions(+), 2610 deletions(-) diff --git a/build.sh b/build.sh index 9678418ea..715ab8a7d 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,7 @@ # # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl.sh b/scripts/mkqtdecl.sh index e76693c35..9a3ff1706 100755 --- a/scripts/mkqtdecl.sh +++ b/scripts/mkqtdecl.sh @@ -20,7 +20,7 @@ # ./scripts/mkqtdecl.sh -h # # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/c++.treetop b/scripts/mkqtdecl_common/c++.treetop index f5a1f845a..233177cdd 100644 --- a/scripts/mkqtdecl_common/c++.treetop +++ b/scripts/mkqtdecl_common/c++.treetop @@ -1,5 +1,5 @@ # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/cpp_classes.rb b/scripts/mkqtdecl_common/cpp_classes.rb index ecbb53e51..95421c3c1 100644 --- a/scripts/mkqtdecl_common/cpp_classes.rb +++ b/scripts/mkqtdecl_common/cpp_classes.rb @@ -1,5 +1,5 @@ # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/cpp_parser_classes.rb b/scripts/mkqtdecl_common/cpp_parser_classes.rb index 9b39ff3f7..d83c201cf 100644 --- a/scripts/mkqtdecl_common/cpp_parser_classes.rb +++ b/scripts/mkqtdecl_common/cpp_parser_classes.rb @@ -1,5 +1,5 @@ # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/dump.rb b/scripts/mkqtdecl_common/dump.rb index 707774c72..ddd25987d 100755 --- a/scripts/mkqtdecl_common/dump.rb +++ b/scripts/mkqtdecl_common/dump.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/mkqtdecl_extract_ambigous_methods.rb b/scripts/mkqtdecl_common/mkqtdecl_extract_ambigous_methods.rb index 113532e9a..ea25c8b64 100644 --- a/scripts/mkqtdecl_common/mkqtdecl_extract_ambigous_methods.rb +++ b/scripts/mkqtdecl_common/mkqtdecl_extract_ambigous_methods.rb @@ -1,6 +1,6 @@ # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/mkqtdecl_extract_nc_pointers.rb b/scripts/mkqtdecl_common/mkqtdecl_extract_nc_pointers.rb index 38e78c4d8..c88315117 100644 --- a/scripts/mkqtdecl_common/mkqtdecl_extract_nc_pointers.rb +++ b/scripts/mkqtdecl_common/mkqtdecl_extract_nc_pointers.rb @@ -1,6 +1,6 @@ # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/mkqtdecl_extract_potential_factories.rb b/scripts/mkqtdecl_common/mkqtdecl_extract_potential_factories.rb index 3069a38f5..b96dc434e 100644 --- a/scripts/mkqtdecl_common/mkqtdecl_extract_potential_factories.rb +++ b/scripts/mkqtdecl_common/mkqtdecl_extract_potential_factories.rb @@ -1,6 +1,6 @@ # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/mkqtdecl_extract_props.rb b/scripts/mkqtdecl_common/mkqtdecl_extract_props.rb index 4ac978bcc..0a35c662a 100644 --- a/scripts/mkqtdecl_common/mkqtdecl_extract_props.rb +++ b/scripts/mkqtdecl_common/mkqtdecl_extract_props.rb @@ -1,6 +1,6 @@ # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/mkqtdecl_extract_signals.rb b/scripts/mkqtdecl_common/mkqtdecl_extract_signals.rb index 905ea8960..f630e7de3 100644 --- a/scripts/mkqtdecl_common/mkqtdecl_extract_signals.rb +++ b/scripts/mkqtdecl_common/mkqtdecl_extract_signals.rb @@ -1,6 +1,6 @@ # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/parse.rb b/scripts/mkqtdecl_common/parse.rb index 26b1357fe..e1c40cab7 100755 --- a/scripts/mkqtdecl_common/parse.rb +++ b/scripts/mkqtdecl_common/parse.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/scripts/mkqtdecl_common/produce.rb b/scripts/mkqtdecl_common/produce.rb index c668c9d31..e8233efba 100755 --- a/scripts/mkqtdecl_common/produce.rb +++ b/scripts/mkqtdecl_common/produce.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 @@ -1421,7 +1421,7 @@ class BindingProducer /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 @@ -2934,7 +2934,7 @@ END /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 @@ -3013,7 +3013,7 @@ END /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/scripts/mkqtdecl_common/reader_ext.rb b/scripts/mkqtdecl_common/reader_ext.rb index 36834aa28..3882ad889 100644 --- a/scripts/mkqtdecl_common/reader_ext.rb +++ b/scripts/mkqtdecl_common/reader_ext.rb @@ -1,6 +1,6 @@ # -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/setup.py b/setup.py index de9790db5..c1ecb6f49 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ KLayout standalone Python module setup script - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antCommon.h b/src/ant/ant/antCommon.h index b08914696..5229d80be 100644 --- a/src/ant/ant/antCommon.h +++ b/src/ant/ant/antCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antConfig.cc b/src/ant/ant/antConfig.cc index 9ee98cb92..80cf6ca7e 100644 --- a/src/ant/ant/antConfig.cc +++ b/src/ant/ant/antConfig.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antConfig.h b/src/ant/ant/antConfig.h index a50852f06..cdc1a5039 100644 --- a/src/ant/ant/antConfig.h +++ b/src/ant/ant/antConfig.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antConfigPage.cc b/src/ant/ant/antConfigPage.cc index 498c066b3..af3b57a08 100644 --- a/src/ant/ant/antConfigPage.cc +++ b/src/ant/ant/antConfigPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antConfigPage.h b/src/ant/ant/antConfigPage.h index f15e48434..1cb65de40 100644 --- a/src/ant/ant/antConfigPage.h +++ b/src/ant/ant/antConfigPage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antForceLink.cc b/src/ant/ant/antForceLink.cc index dcd7f17cd..78de16b6d 100644 --- a/src/ant/ant/antForceLink.cc +++ b/src/ant/ant/antForceLink.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antForceLink.h b/src/ant/ant/antForceLink.h index 147b65eb1..c565750ca 100644 --- a/src/ant/ant/antForceLink.h +++ b/src/ant/ant/antForceLink.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antObject.cc b/src/ant/ant/antObject.cc index 686224fc9..94bb74218 100644 --- a/src/ant/ant/antObject.cc +++ b/src/ant/ant/antObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antObject.h b/src/ant/ant/antObject.h index 4c09a27ad..45190c04b 100644 --- a/src/ant/ant/antObject.h +++ b/src/ant/ant/antObject.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antPlugin.cc b/src/ant/ant/antPlugin.cc index 1bd0d327e..6df9361c9 100644 --- a/src/ant/ant/antPlugin.cc +++ b/src/ant/ant/antPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antPlugin.h b/src/ant/ant/antPlugin.h index 6ca84f54d..98ba98d72 100644 --- a/src/ant/ant/antPlugin.h +++ b/src/ant/ant/antPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antPropertiesPage.cc b/src/ant/ant/antPropertiesPage.cc index cc7ea697f..35c7e1d79 100644 --- a/src/ant/ant/antPropertiesPage.cc +++ b/src/ant/ant/antPropertiesPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antPropertiesPage.h b/src/ant/ant/antPropertiesPage.h index 837df41d1..9e71ac471 100644 --- a/src/ant/ant/antPropertiesPage.h +++ b/src/ant/ant/antPropertiesPage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index 60161e266..af310c2e5 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h index 7dd8b8e12..67226e3fa 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antTemplate.cc b/src/ant/ant/antTemplate.cc index 818befc0e..907a4de66 100644 --- a/src/ant/ant/antTemplate.cc +++ b/src/ant/ant/antTemplate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/antTemplate.h b/src/ant/ant/antTemplate.h index fe150810f..cc8a6ec1c 100644 --- a/src/ant/ant/antTemplate.h +++ b/src/ant/ant/antTemplate.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/ant/gsiDeclAnt.cc b/src/ant/ant/gsiDeclAnt.cc index 62767d378..af42ac044 100644 --- a/src/ant/ant/gsiDeclAnt.cc +++ b/src/ant/ant/gsiDeclAnt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/ant/unit_tests/antBasicTests.cc b/src/ant/unit_tests/antBasicTests.cc index 4a405737a..df9c664c8 100644 --- a/src/ant/unit_tests/antBasicTests.cc +++ b/src/ant/unit_tests/antBasicTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/bdCommon.h b/src/buddies/src/bd/bdCommon.h index 8ad9b7595..359efa5b0 100644 --- a/src/buddies/src/bd/bdCommon.h +++ b/src/buddies/src/bd/bdCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/bdConverterMain.cc b/src/buddies/src/bd/bdConverterMain.cc index 1ecbf8de7..2fe62d911 100644 --- a/src/buddies/src/bd/bdConverterMain.cc +++ b/src/buddies/src/bd/bdConverterMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/bdConverterMain.h b/src/buddies/src/bd/bdConverterMain.h index d743ec2a9..2a12776ce 100644 --- a/src/buddies/src/bd/bdConverterMain.h +++ b/src/buddies/src/bd/bdConverterMain.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/bdInit.cc b/src/buddies/src/bd/bdInit.cc index 3b91672d1..ddfc7051e 100644 --- a/src/buddies/src/bd/bdInit.cc +++ b/src/buddies/src/bd/bdInit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/bdInit.h b/src/buddies/src/bd/bdInit.h index ab2fd5fe0..f52027d41 100644 --- a/src/buddies/src/bd/bdInit.h +++ b/src/buddies/src/bd/bdInit.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index f5832d279..c7fc839d8 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/bdReaderOptions.h b/src/buddies/src/bd/bdReaderOptions.h index fe71b310f..7158d0552 100644 --- a/src/buddies/src/bd/bdReaderOptions.h +++ b/src/buddies/src/bd/bdReaderOptions.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/bdWriterOptions.cc b/src/buddies/src/bd/bdWriterOptions.cc index 2176416b4..d10ac5acf 100644 --- a/src/buddies/src/bd/bdWriterOptions.cc +++ b/src/buddies/src/bd/bdWriterOptions.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/bdWriterOptions.h b/src/buddies/src/bd/bdWriterOptions.h index 87104c612..197b073c9 100644 --- a/src/buddies/src/bd/bdWriterOptions.h +++ b/src/buddies/src/bd/bdWriterOptions.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/main.cc b/src/buddies/src/bd/main.cc index c8f9aa2f7..07e819faf 100644 --- a/src/buddies/src/bd/main.cc +++ b/src/buddies/src/bd/main.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/strm2cif.cc b/src/buddies/src/bd/strm2cif.cc index 77f3e43ce..437e75bda 100644 --- a/src/buddies/src/bd/strm2cif.cc +++ b/src/buddies/src/bd/strm2cif.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/strm2dxf.cc b/src/buddies/src/bd/strm2dxf.cc index 4ddc26cda..9c21f2dd9 100644 --- a/src/buddies/src/bd/strm2dxf.cc +++ b/src/buddies/src/bd/strm2dxf.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/strm2gds.cc b/src/buddies/src/bd/strm2gds.cc index b9d8ebdc1..67a639965 100644 --- a/src/buddies/src/bd/strm2gds.cc +++ b/src/buddies/src/bd/strm2gds.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/strm2gdstxt.cc b/src/buddies/src/bd/strm2gdstxt.cc index 590e49a33..01f9077b4 100644 --- a/src/buddies/src/bd/strm2gdstxt.cc +++ b/src/buddies/src/bd/strm2gdstxt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/strm2oas.cc b/src/buddies/src/bd/strm2oas.cc index f70ba59c9..9fe00c482 100644 --- a/src/buddies/src/bd/strm2oas.cc +++ b/src/buddies/src/bd/strm2oas.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/strm2txt.cc b/src/buddies/src/bd/strm2txt.cc index 4c5b608dc..5f907b436 100644 --- a/src/buddies/src/bd/strm2txt.cc +++ b/src/buddies/src/bd/strm2txt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/strmclip.cc b/src/buddies/src/bd/strmclip.cc index 918b93eac..c153704e9 100644 --- a/src/buddies/src/bd/strmclip.cc +++ b/src/buddies/src/bd/strmclip.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/strmcmp.cc b/src/buddies/src/bd/strmcmp.cc index aae8288c1..70470844a 100644 --- a/src/buddies/src/bd/strmcmp.cc +++ b/src/buddies/src/bd/strmcmp.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/strmrun.cc b/src/buddies/src/bd/strmrun.cc index 3ddbbeb2c..9437162be 100644 --- a/src/buddies/src/bd/strmrun.cc +++ b/src/buddies/src/bd/strmrun.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 9c0f845bf..13fa5d570 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/unit_tests/bdBasicTests.cc b/src/buddies/unit_tests/bdBasicTests.cc index f79edb7f1..312bb12b5 100644 --- a/src/buddies/unit_tests/bdBasicTests.cc +++ b/src/buddies/unit_tests/bdBasicTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/unit_tests/bdConverterTests.cc b/src/buddies/unit_tests/bdConverterTests.cc index c05e69582..739d40b13 100644 --- a/src/buddies/unit_tests/bdConverterTests.cc +++ b/src/buddies/unit_tests/bdConverterTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/unit_tests/bdStrm2txtTests.cc b/src/buddies/unit_tests/bdStrm2txtTests.cc index 5fbc7ca4d..791c1877c 100644 --- a/src/buddies/unit_tests/bdStrm2txtTests.cc +++ b/src/buddies/unit_tests/bdStrm2txtTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/unit_tests/bdStrmclipTests.cc b/src/buddies/unit_tests/bdStrmclipTests.cc index 515b9521a..fe4083732 100644 --- a/src/buddies/unit_tests/bdStrmclipTests.cc +++ b/src/buddies/unit_tests/bdStrmclipTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/unit_tests/bdStrmcmpTests.cc b/src/buddies/unit_tests/bdStrmcmpTests.cc index b0fff0316..8e3efdcd1 100644 --- a/src/buddies/unit_tests/bdStrmcmpTests.cc +++ b/src/buddies/unit_tests/bdStrmcmpTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/unit_tests/bdStrmrunTests.cc b/src/buddies/unit_tests/bdStrmrunTests.cc index 1d4d1421e..eda168287 100644 --- a/src/buddies/unit_tests/bdStrmrunTests.cc +++ b/src/buddies/unit_tests/bdStrmrunTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/buddies/unit_tests/bdStrmxorTests.cc b/src/buddies/unit_tests/bdStrmxorTests.cc index b795da6ce..27871f521 100644 --- a/src/buddies/unit_tests/bdStrmxorTests.cc +++ b/src/buddies/unit_tests/bdStrmxorTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbArray.cc b/src/db/db/dbArray.cc index 41558709d..e27e7d5e7 100644 --- a/src/db/db/dbArray.cc +++ b/src/db/db/dbArray.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index b2e8cdff4..8e9f1b6e0 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc index d23d308fa..387662a45 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.cc +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbAsIfFlatEdgePairs.h b/src/db/db/dbAsIfFlatEdgePairs.h index 3913964cc..93f8fec63 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.h +++ b/src/db/db/dbAsIfFlatEdgePairs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 2b709189a..20bcae8e3 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbAsIfFlatEdges.h b/src/db/db/dbAsIfFlatEdges.h index adead24af..e1541d75c 100644 --- a/src/db/db/dbAsIfFlatEdges.h +++ b/src/db/db/dbAsIfFlatEdges.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 46af6c33b..c5b4c3b64 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index b932903e4..9353d5d80 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbBox.cc b/src/db/db/dbBox.cc index 1e3f868d9..7f9288002 100644 --- a/src/db/db/dbBox.cc +++ b/src/db/db/dbBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbBox.h b/src/db/db/dbBox.h index a3319fffb..a3445e3cf 100644 --- a/src/db/db/dbBox.h +++ b/src/db/db/dbBox.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbBoxConvert.cc b/src/db/db/dbBoxConvert.cc index 0076a9ead..6108cdaa0 100644 --- a/src/db/db/dbBoxConvert.cc +++ b/src/db/db/dbBoxConvert.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbBoxConvert.h b/src/db/db/dbBoxConvert.h index ef95362d6..a5b3f6b28 100644 --- a/src/db/db/dbBoxConvert.h +++ b/src/db/db/dbBoxConvert.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbBoxScanner.cc b/src/db/db/dbBoxScanner.cc index 9fd8b4ae2..4daf8750a 100644 --- a/src/db/db/dbBoxScanner.cc +++ b/src/db/db/dbBoxScanner.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index 5322a0686..0f8f206f1 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbBoxTree.h b/src/db/db/dbBoxTree.h index b560b5b03..0219357f0 100644 --- a/src/db/db/dbBoxTree.h +++ b/src/db/db/dbBoxTree.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index 8d1499b75..a3f95ff79 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCell.h b/src/db/db/dbCell.h index 370034eda..2d3574cbb 100644 --- a/src/db/db/dbCell.h +++ b/src/db/db/dbCell.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCellGraphUtils.cc b/src/db/db/dbCellGraphUtils.cc index d3a7b6e03..e0c34dc59 100644 --- a/src/db/db/dbCellGraphUtils.cc +++ b/src/db/db/dbCellGraphUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCellGraphUtils.h b/src/db/db/dbCellGraphUtils.h index 7c7a0b185..b2cedd777 100644 --- a/src/db/db/dbCellGraphUtils.h +++ b/src/db/db/dbCellGraphUtils.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCellHullGenerator.cc b/src/db/db/dbCellHullGenerator.cc index c9e4842c3..abca8f24f 100644 --- a/src/db/db/dbCellHullGenerator.cc +++ b/src/db/db/dbCellHullGenerator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCellHullGenerator.h b/src/db/db/dbCellHullGenerator.h index d0d979a43..a7a094a00 100644 --- a/src/db/db/dbCellHullGenerator.h +++ b/src/db/db/dbCellHullGenerator.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCellInst.cc b/src/db/db/dbCellInst.cc index 49a6d4ad0..cbdac63f4 100644 --- a/src/db/db/dbCellInst.cc +++ b/src/db/db/dbCellInst.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCellInst.h b/src/db/db/dbCellInst.h index 0eda9b452..8ae02bf41 100644 --- a/src/db/db/dbCellInst.h +++ b/src/db/db/dbCellInst.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index 0bfc9c69c..f942981bd 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCellMapping.h b/src/db/db/dbCellMapping.h index c50001a0c..854fd1ffa 100644 --- a/src/db/db/dbCellMapping.h +++ b/src/db/db/dbCellMapping.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbClip.cc b/src/db/db/dbClip.cc index 8321b1c19..6544ba98e 100644 --- a/src/db/db/dbClip.cc +++ b/src/db/db/dbClip.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbClip.h b/src/db/db/dbClip.h index 1ab87063c..68912b566 100644 --- a/src/db/db/dbClip.h +++ b/src/db/db/dbClip.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbClipboard.cc b/src/db/db/dbClipboard.cc index 6dbf51758..6c611ee38 100644 --- a/src/db/db/dbClipboard.cc +++ b/src/db/db/dbClipboard.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbClipboard.h b/src/db/db/dbClipboard.h index 6fae578a5..bf333c069 100644 --- a/src/db/db/dbClipboard.h +++ b/src/db/db/dbClipboard.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbClipboardData.cc b/src/db/db/dbClipboardData.cc index a5af2b916..32655c131 100644 --- a/src/db/db/dbClipboardData.cc +++ b/src/db/db/dbClipboardData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbClipboardData.h b/src/db/db/dbClipboardData.h index 8a71215f0..41ca05c0b 100644 --- a/src/db/db/dbClipboardData.h +++ b/src/db/db/dbClipboardData.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCommon.h b/src/db/db/dbCommon.h index b929e036f..16d2744f4 100644 --- a/src/db/db/dbCommon.h +++ b/src/db/db/dbCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index 06926dd7e..1b96578b8 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbCommonReader.h b/src/db/db/dbCommonReader.h index 0bd4d5efd..075c42608 100644 --- a/src/db/db/dbCommonReader.h +++ b/src/db/db/dbCommonReader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbConverters.cc b/src/db/db/dbConverters.cc index 77abf43c0..7bf766b1d 100644 --- a/src/db/db/dbConverters.cc +++ b/src/db/db/dbConverters.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbConverters.h b/src/db/db/dbConverters.h index e8b846095..c827d0813 100644 --- a/src/db/db/dbConverters.h +++ b/src/db/db/dbConverters.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 1b8eb3dec..17535ebad 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 8f6c8a909..00db14b56 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 1ca9af602..70f23be24 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 7dac3f9d1..89d109cdd 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdge.cc b/src/db/db/dbEdge.cc index 3af27263f..d72b2c61b 100644 --- a/src/db/db/dbEdge.cc +++ b/src/db/db/dbEdge.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdge.h b/src/db/db/dbEdge.h index 15d0cbcdf..e3959c620 100644 --- a/src/db/db/dbEdge.h +++ b/src/db/db/dbEdge.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgeBoolean.cc b/src/db/db/dbEdgeBoolean.cc index 52a6ec7de..546257bb0 100644 --- a/src/db/db/dbEdgeBoolean.cc +++ b/src/db/db/dbEdgeBoolean.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgeBoolean.h b/src/db/db/dbEdgeBoolean.h index b9e794eb0..126960546 100644 --- a/src/db/db/dbEdgeBoolean.h +++ b/src/db/db/dbEdgeBoolean.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgePair.cc b/src/db/db/dbEdgePair.cc index d4055329b..30914c0ff 100644 --- a/src/db/db/dbEdgePair.cc +++ b/src/db/db/dbEdgePair.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgePair.h b/src/db/db/dbEdgePair.h index 3ce03c3ef..6704abe16 100644 --- a/src/db/db/dbEdgePair.h +++ b/src/db/db/dbEdgePair.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgePairRelations.cc b/src/db/db/dbEdgePairRelations.cc index 345e04066..2c60966bb 100644 --- a/src/db/db/dbEdgePairRelations.cc +++ b/src/db/db/dbEdgePairRelations.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgePairRelations.h b/src/db/db/dbEdgePairRelations.h index 715c8ea6f..ed06741c4 100644 --- a/src/db/db/dbEdgePairRelations.h +++ b/src/db/db/dbEdgePairRelations.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index 6e0f5cbb7..cc9ca5d29 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 93b9efa1a..e34d6cdde 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgePairsDelegate.cc b/src/db/db/dbEdgePairsDelegate.cc index 5f83bb58f..470b8f3e4 100644 --- a/src/db/db/dbEdgePairsDelegate.cc +++ b/src/db/db/dbEdgePairsDelegate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgePairsDelegate.h b/src/db/db/dbEdgePairsDelegate.h index 8f979e4d1..a1f0758a2 100644 --- a/src/db/db/dbEdgePairsDelegate.h +++ b/src/db/db/dbEdgePairsDelegate.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 00025fd8a..0a1481cd1 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index 7170e1b19..bca103bc2 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index f6b78eb3a..4ed67922e 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index df85cdac9..efdb7c4b8 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgesDelegate.cc b/src/db/db/dbEdgesDelegate.cc index b545e2fb2..65f840892 100644 --- a/src/db/db/dbEdgesDelegate.cc +++ b/src/db/db/dbEdgesDelegate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 5f97137fe..a647062be 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgesToContours.cc b/src/db/db/dbEdgesToContours.cc index 832d5cb39..5390e3c32 100644 --- a/src/db/db/dbEdgesToContours.cc +++ b/src/db/db/dbEdgesToContours.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEdgesToContours.h b/src/db/db/dbEdgesToContours.h index b22cf4c44..b4980b83d 100644 --- a/src/db/db/dbEdgesToContours.h +++ b/src/db/db/dbEdgesToContours.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEmptyEdgePairs.cc b/src/db/db/dbEmptyEdgePairs.cc index e806c34f6..d26aa0956 100644 --- a/src/db/db/dbEmptyEdgePairs.cc +++ b/src/db/db/dbEmptyEdgePairs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEmptyEdgePairs.h b/src/db/db/dbEmptyEdgePairs.h index 01281d8d6..104bbc63d 100644 --- a/src/db/db/dbEmptyEdgePairs.h +++ b/src/db/db/dbEmptyEdgePairs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEmptyEdges.cc b/src/db/db/dbEmptyEdges.cc index f8f8174a0..fb1dfbc8a 100644 --- a/src/db/db/dbEmptyEdges.cc +++ b/src/db/db/dbEmptyEdges.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h index 21833d08f..24dd9e17d 100644 --- a/src/db/db/dbEmptyEdges.h +++ b/src/db/db/dbEmptyEdges.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEmptyRegion.cc b/src/db/db/dbEmptyRegion.cc index 6389faa51..35e09021d 100644 --- a/src/db/db/dbEmptyRegion.cc +++ b/src/db/db/dbEmptyRegion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index da0327ba0..ddde91409 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbFillTool.cc b/src/db/db/dbFillTool.cc index 7cdfc20b3..ce019e0d4 100644 --- a/src/db/db/dbFillTool.cc +++ b/src/db/db/dbFillTool.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbFillTool.h b/src/db/db/dbFillTool.h index e7839c508..ae9672582 100644 --- a/src/db/db/dbFillTool.h +++ b/src/db/db/dbFillTool.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc index 9c4c66747..dd465c9c2 100644 --- a/src/db/db/dbFlatEdgePairs.cc +++ b/src/db/db/dbFlatEdgePairs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbFlatEdgePairs.h b/src/db/db/dbFlatEdgePairs.h index c0c2d96f2..a9d87fe6b 100644 --- a/src/db/db/dbFlatEdgePairs.h +++ b/src/db/db/dbFlatEdgePairs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index c1e3e4606..a83f88a2e 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbFlatEdges.h b/src/db/db/dbFlatEdges.h index 8a034f41f..bb19fa05b 100644 --- a/src/db/db/dbFlatEdges.h +++ b/src/db/db/dbFlatEdges.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index ae1868c23..52d6ba07f 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index cbae0ba71..c3037080a 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbForceLink.cc b/src/db/db/dbForceLink.cc index 238eac374..aafe3aff5 100644 --- a/src/db/db/dbForceLink.cc +++ b/src/db/db/dbForceLink.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbForceLink.h b/src/db/db/dbForceLink.h index 25d239016..401d0ab69 100644 --- a/src/db/db/dbForceLink.h +++ b/src/db/db/dbForceLink.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbFuzzyCellMapping.cc b/src/db/db/dbFuzzyCellMapping.cc index 166396a1d..b2df293f0 100644 --- a/src/db/db/dbFuzzyCellMapping.cc +++ b/src/db/db/dbFuzzyCellMapping.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbFuzzyCellMapping.h b/src/db/db/dbFuzzyCellMapping.h index dcdedf538..2ff65f514 100644 --- a/src/db/db/dbFuzzyCellMapping.h +++ b/src/db/db/dbFuzzyCellMapping.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbGlyphs.cc b/src/db/db/dbGlyphs.cc index 30ed1509a..5b4b28839 100644 --- a/src/db/db/dbGlyphs.cc +++ b/src/db/db/dbGlyphs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbGlyphs.h b/src/db/db/dbGlyphs.h index 9e7b0d702..56af413cd 100644 --- a/src/db/db/dbGlyphs.h +++ b/src/db/db/dbGlyphs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index dca49e959..ef46366d3 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbHershey.cc b/src/db/db/dbHershey.cc index 6e303edc7..194019666 100644 --- a/src/db/db/dbHershey.cc +++ b/src/db/db/dbHershey.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbHershey.h b/src/db/db/dbHershey.h index 88e8e1bfd..14e0677f2 100644 --- a/src/db/db/dbHershey.h +++ b/src/db/db/dbHershey.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbHersheyFont.h b/src/db/db/dbHersheyFont.h index 315464d2d..72161755e 100644 --- a/src/db/db/dbHersheyFont.h +++ b/src/db/db/dbHersheyFont.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 91c5aaab0..71b74530b 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index b33801e9f..d4211f9ce 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 6f105a063..87d95c4b3 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index 71128dec4..2918c4d95 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 5dc23833d..8c4e409ea 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 5e0da406f..796cf514b 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbInit.cc b/src/db/db/dbInit.cc index 935363a52..2f6a462d1 100644 --- a/src/db/db/dbInit.cc +++ b/src/db/db/dbInit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbInit.h b/src/db/db/dbInit.h index 11f9f93a1..517c162fc 100644 --- a/src/db/db/dbInit.h +++ b/src/db/db/dbInit.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbInstElement.cc b/src/db/db/dbInstElement.cc index cb1420118..ad16a707a 100644 --- a/src/db/db/dbInstElement.cc +++ b/src/db/db/dbInstElement.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbInstElement.h b/src/db/db/dbInstElement.h index 6a81e30aa..134afe9ac 100644 --- a/src/db/db/dbInstElement.h +++ b/src/db/db/dbInstElement.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbInstances.cc b/src/db/db/dbInstances.cc index ba5f8925a..2022b8e2a 100644 --- a/src/db/db/dbInstances.cc +++ b/src/db/db/dbInstances.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbInstances.h b/src/db/db/dbInstances.h index b1642b120..a1daba495 100644 --- a/src/db/db/dbInstances.h +++ b/src/db/db/dbInstances.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayer.h b/src/db/db/dbLayer.h index 9a6513aaf..db5165e41 100644 --- a/src/db/db/dbLayer.h +++ b/src/db/db/dbLayer.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayerMapping.cc b/src/db/db/dbLayerMapping.cc index 14e760e1a..5da41120e 100644 --- a/src/db/db/dbLayerMapping.cc +++ b/src/db/db/dbLayerMapping.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayerMapping.h b/src/db/db/dbLayerMapping.h index dc9fec28f..51ff32d3e 100644 --- a/src/db/db/dbLayerMapping.h +++ b/src/db/db/dbLayerMapping.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayerProperties.cc b/src/db/db/dbLayerProperties.cc index 85b758fa2..a435c2ee4 100644 --- a/src/db/db/dbLayerProperties.cc +++ b/src/db/db/dbLayerProperties.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayerProperties.h b/src/db/db/dbLayerProperties.h index b560311fa..7a944041b 100644 --- a/src/db/db/dbLayerProperties.h +++ b/src/db/db/dbLayerProperties.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 91fd9f995..1bbfd432b 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 76ecb0a7c..40ae5ad2d 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutContextHandler.cc b/src/db/db/dbLayoutContextHandler.cc index 9ac10f5ac..f5ffa323d 100644 --- a/src/db/db/dbLayoutContextHandler.cc +++ b/src/db/db/dbLayoutContextHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutContextHandler.h b/src/db/db/dbLayoutContextHandler.h index 15c0ddd81..2e85193df 100644 --- a/src/db/db/dbLayoutContextHandler.h +++ b/src/db/db/dbLayoutContextHandler.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutDiff.cc b/src/db/db/dbLayoutDiff.cc index f95e760cf..a5565152d 100644 --- a/src/db/db/dbLayoutDiff.cc +++ b/src/db/db/dbLayoutDiff.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutDiff.h b/src/db/db/dbLayoutDiff.h index 713319824..afeebed1e 100644 --- a/src/db/db/dbLayoutDiff.h +++ b/src/db/db/dbLayoutDiff.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutQuery.cc b/src/db/db/dbLayoutQuery.cc index e09c9b25a..0bc0c3997 100644 --- a/src/db/db/dbLayoutQuery.cc +++ b/src/db/db/dbLayoutQuery.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutQuery.h b/src/db/db/dbLayoutQuery.h index 77ea1c675..bcaf81d4c 100644 --- a/src/db/db/dbLayoutQuery.h +++ b/src/db/db/dbLayoutQuery.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutStateModel.cc b/src/db/db/dbLayoutStateModel.cc index 673f7135e..4681cbdca 100644 --- a/src/db/db/dbLayoutStateModel.cc +++ b/src/db/db/dbLayoutStateModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutStateModel.h b/src/db/db/dbLayoutStateModel.h index 8bb90fa3d..7ccc3e07f 100644 --- a/src/db/db/dbLayoutStateModel.h +++ b/src/db/db/dbLayoutStateModel.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index f6cceff9c..c70fcef69 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -3,7 +3,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index ad6c8f033..4668bb9e6 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index b9bbc2431..503ab35bc 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLayoutUtils.h b/src/db/db/dbLayoutUtils.h index 5ea0f952b..ae27790f2 100644 --- a/src/db/db/dbLayoutUtils.h +++ b/src/db/db/dbLayoutUtils.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index 6c38e46ac..7ea6b28ed 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLibrary.h b/src/db/db/dbLibrary.h index 463a5c125..9798f381e 100644 --- a/src/db/db/dbLibrary.h +++ b/src/db/db/dbLibrary.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLibraryManager.cc b/src/db/db/dbLibraryManager.cc index f3fb61b2f..c1c0d4bf8 100644 --- a/src/db/db/dbLibraryManager.cc +++ b/src/db/db/dbLibraryManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLibraryManager.h b/src/db/db/dbLibraryManager.h index dae830364..26e3954b0 100644 --- a/src/db/db/dbLibraryManager.h +++ b/src/db/db/dbLibraryManager.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLibraryProxy.cc b/src/db/db/dbLibraryProxy.cc index 79d452a1e..85eaf17f7 100644 --- a/src/db/db/dbLibraryProxy.cc +++ b/src/db/db/dbLibraryProxy.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLibraryProxy.h b/src/db/db/dbLibraryProxy.h index 2771dab99..26ed25d5a 100644 --- a/src/db/db/dbLibraryProxy.h +++ b/src/db/db/dbLibraryProxy.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLoadLayoutOptions.cc b/src/db/db/dbLoadLayoutOptions.cc index 7e8aa8206..84f1ab4bc 100644 --- a/src/db/db/dbLoadLayoutOptions.cc +++ b/src/db/db/dbLoadLayoutOptions.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLoadLayoutOptions.h b/src/db/db/dbLoadLayoutOptions.h index 3a8452951..21a5380c9 100644 --- a/src/db/db/dbLoadLayoutOptions.h +++ b/src/db/db/dbLoadLayoutOptions.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index 95efbb377..acedde581 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h index c80086fdc..73c2ba242 100644 --- a/src/db/db/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbManager.cc b/src/db/db/dbManager.cc index c9d60ec95..51dec8ab9 100644 --- a/src/db/db/dbManager.cc +++ b/src/db/db/dbManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbManager.h b/src/db/db/dbManager.h index 6deed2e74..de523e23e 100644 --- a/src/db/db/dbManager.h +++ b/src/db/db/dbManager.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbMatrix.cc b/src/db/db/dbMatrix.cc index e7572fc4b..279b5522b 100644 --- a/src/db/db/dbMatrix.cc +++ b/src/db/db/dbMatrix.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbMatrix.h b/src/db/db/dbMatrix.h index 8777df625..912cce6f5 100644 --- a/src/db/db/dbMatrix.h +++ b/src/db/db/dbMatrix.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbMemStatistics.cc b/src/db/db/dbMemStatistics.cc index 6f15d7d65..20fb56c01 100644 --- a/src/db/db/dbMemStatistics.cc +++ b/src/db/db/dbMemStatistics.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbMemStatistics.h b/src/db/db/dbMemStatistics.h index 9ae6bd4aa..ee7e4c66e 100644 --- a/src/db/db/dbMemStatistics.h +++ b/src/db/db/dbMemStatistics.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbMetaInfo.h b/src/db/db/dbMetaInfo.h index 60ca12908..a3f92a552 100644 --- a/src/db/db/dbMetaInfo.h +++ b/src/db/db/dbMetaInfo.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNamedLayerReader.cc b/src/db/db/dbNamedLayerReader.cc index 5e7fcae46..a59f68ab9 100644 --- a/src/db/db/dbNamedLayerReader.cc +++ b/src/db/db/dbNamedLayerReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNamedLayerReader.h b/src/db/db/dbNamedLayerReader.h index c8a8d0eac..ff09d1e2f 100644 --- a/src/db/db/dbNamedLayerReader.h +++ b/src/db/db/dbNamedLayerReader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 914f6a882..8ca118e60 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 1c6acb2ad..c944f325a 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index 7b61d6a09..393653d05 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index 95408a2dd..fca6d3c15 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 210351de2..16bac404e 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index de7201669..a2a845eb5 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index 0d3dafbd9..712ae0b9f 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.h b/src/db/db/dbNetlistDeviceExtractorClasses.h index b779317d5..904bf37dd 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.h +++ b/src/db/db/dbNetlistDeviceExtractorClasses.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 38ae498df..538e46d39 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index 2e87113c8..30a48047f 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlistProperty.cc b/src/db/db/dbNetlistProperty.cc index c34f67407..1680c86d7 100644 --- a/src/db/db/dbNetlistProperty.cc +++ b/src/db/db/dbNetlistProperty.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbNetlistProperty.h b/src/db/db/dbNetlistProperty.h index be8d92ca1..1eff42c23 100644 --- a/src/db/db/dbNetlistProperty.h +++ b/src/db/db/dbNetlistProperty.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbObject.cc b/src/db/db/dbObject.cc index 5350213f6..e2f19a06a 100644 --- a/src/db/db/dbObject.cc +++ b/src/db/db/dbObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbObject.h b/src/db/db/dbObject.h index 1a143c868..8fa84cbd3 100644 --- a/src/db/db/dbObject.h +++ b/src/db/db/dbObject.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbObjectTag.h b/src/db/db/dbObjectTag.h index 85ee51aa3..743d71ca9 100644 --- a/src/db/db/dbObjectTag.h +++ b/src/db/db/dbObjectTag.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbObjectWithProperties.h b/src/db/db/dbObjectWithProperties.h index d8afb6818..ed7fa6a70 100644 --- a/src/db/db/dbObjectWithProperties.h +++ b/src/db/db/dbObjectWithProperties.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbOriginalLayerEdgePairs.cc b/src/db/db/dbOriginalLayerEdgePairs.cc index 6791e069e..76b9aaa6d 100644 --- a/src/db/db/dbOriginalLayerEdgePairs.cc +++ b/src/db/db/dbOriginalLayerEdgePairs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbOriginalLayerEdgePairs.h b/src/db/db/dbOriginalLayerEdgePairs.h index 9d7ada01c..763da04a6 100644 --- a/src/db/db/dbOriginalLayerEdgePairs.h +++ b/src/db/db/dbOriginalLayerEdgePairs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc index 52e00afa7..ee47f5771 100644 --- a/src/db/db/dbOriginalLayerEdges.cc +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbOriginalLayerEdges.h b/src/db/db/dbOriginalLayerEdges.h index 13a4d70c5..883e21a82 100644 --- a/src/db/db/dbOriginalLayerEdges.h +++ b/src/db/db/dbOriginalLayerEdges.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc index b1bd13585..025c5df5f 100644 --- a/src/db/db/dbOriginalLayerRegion.cc +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbOriginalLayerRegion.h b/src/db/db/dbOriginalLayerRegion.h index b712e2843..e8b2097bf 100644 --- a/src/db/db/dbOriginalLayerRegion.h +++ b/src/db/db/dbOriginalLayerRegion.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPCellDeclaration.cc b/src/db/db/dbPCellDeclaration.cc index 3a72b1aeb..cb579aea0 100644 --- a/src/db/db/dbPCellDeclaration.cc +++ b/src/db/db/dbPCellDeclaration.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPCellDeclaration.h b/src/db/db/dbPCellDeclaration.h index 90f041212..403e7bec6 100644 --- a/src/db/db/dbPCellDeclaration.h +++ b/src/db/db/dbPCellDeclaration.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPCellHeader.cc b/src/db/db/dbPCellHeader.cc index fbcfdcc76..dfc2ab958 100644 --- a/src/db/db/dbPCellHeader.cc +++ b/src/db/db/dbPCellHeader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPCellHeader.h b/src/db/db/dbPCellHeader.h index 285db6d7c..e49a41f03 100644 --- a/src/db/db/dbPCellHeader.h +++ b/src/db/db/dbPCellHeader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPCellVariant.cc b/src/db/db/dbPCellVariant.cc index 55f67a840..c2581a748 100644 --- a/src/db/db/dbPCellVariant.cc +++ b/src/db/db/dbPCellVariant.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPCellVariant.h b/src/db/db/dbPCellVariant.h index 8ddc1d38a..f29f11d18 100644 --- a/src/db/db/dbPCellVariant.h +++ b/src/db/db/dbPCellVariant.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPath.cc b/src/db/db/dbPath.cc index 7b50e1ccd..bb992ebdf 100644 --- a/src/db/db/dbPath.cc +++ b/src/db/db/dbPath.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPath.h b/src/db/db/dbPath.h index 4a00b7bb0..30e92e290 100644 --- a/src/db/db/dbPath.h +++ b/src/db/db/dbPath.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPlugin.h b/src/db/db/dbPlugin.h index be4502c94..261b51d00 100644 --- a/src/db/db/dbPlugin.h +++ b/src/db/db/dbPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPoint.cc b/src/db/db/dbPoint.cc index 99c7b4006..d7b340acb 100644 --- a/src/db/db/dbPoint.cc +++ b/src/db/db/dbPoint.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPoint.h b/src/db/db/dbPoint.h index 1392ebdca..23c157afa 100644 --- a/src/db/db/dbPoint.h +++ b/src/db/db/dbPoint.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPolygon.cc b/src/db/db/dbPolygon.cc index f495119b1..d5a030dd3 100644 --- a/src/db/db/dbPolygon.cc +++ b/src/db/db/dbPolygon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPolygon.h b/src/db/db/dbPolygon.h index 94941f6e7..639d696f4 100644 --- a/src/db/db/dbPolygon.h +++ b/src/db/db/dbPolygon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPolygonGenerators.cc b/src/db/db/dbPolygonGenerators.cc index dcb9cf274..c98638481 100644 --- a/src/db/db/dbPolygonGenerators.cc +++ b/src/db/db/dbPolygonGenerators.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPolygonGenerators.h b/src/db/db/dbPolygonGenerators.h index 15033973b..0c48add15 100644 --- a/src/db/db/dbPolygonGenerators.h +++ b/src/db/db/dbPolygonGenerators.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index e44ecfa8b..5873ed3c5 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPolygonTools.h b/src/db/db/dbPolygonTools.h index 88f72cadc..0f915cd0d 100644 --- a/src/db/db/dbPolygonTools.h +++ b/src/db/db/dbPolygonTools.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPropertiesRepository.cc b/src/db/db/dbPropertiesRepository.cc index 0f421413b..252b5c40d 100644 --- a/src/db/db/dbPropertiesRepository.cc +++ b/src/db/db/dbPropertiesRepository.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbPropertiesRepository.h b/src/db/db/dbPropertiesRepository.h index ed8a051e6..fd75e468b 100644 --- a/src/db/db/dbPropertiesRepository.h +++ b/src/db/db/dbPropertiesRepository.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbReader.cc b/src/db/db/dbReader.cc index bd5f28d1c..1d331927a 100644 --- a/src/db/db/dbReader.cc +++ b/src/db/db/dbReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbReader.h b/src/db/db/dbReader.h index c4a12b42d..0140debe3 100644 --- a/src/db/db/dbReader.h +++ b/src/db/db/dbReader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 6c1bfd0ed..92bbc5278 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 6f0a205e0..90a0102ec 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index b9ced87ec..2c0f9bbd5 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 8b3673feb..1165bae84 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbRegionDelegate.cc b/src/db/db/dbRegionDelegate.cc index 42df393ad..fd1392ec6 100644 --- a/src/db/db/dbRegionDelegate.cc +++ b/src/db/db/dbRegionDelegate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index 626317f50..d0a971946 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbSaveLayoutOptions.cc b/src/db/db/dbSaveLayoutOptions.cc index 19155e2af..5dd4e5c8e 100644 --- a/src/db/db/dbSaveLayoutOptions.cc +++ b/src/db/db/dbSaveLayoutOptions.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbSaveLayoutOptions.h b/src/db/db/dbSaveLayoutOptions.h index 77f49a808..b6cc74d33 100644 --- a/src/db/db/dbSaveLayoutOptions.h +++ b/src/db/db/dbSaveLayoutOptions.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShape.cc b/src/db/db/dbShape.cc index f09365a80..3b6a0bfee 100644 --- a/src/db/db/dbShape.cc +++ b/src/db/db/dbShape.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShape.h b/src/db/db/dbShape.h index 76220df8b..1666ca297 100644 --- a/src/db/db/dbShape.h +++ b/src/db/db/dbShape.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShapeIterator.cc b/src/db/db/dbShapeIterator.cc index 4091dbf5c..2acf3f924 100644 --- a/src/db/db/dbShapeIterator.cc +++ b/src/db/db/dbShapeIterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShapeProcessor.cc b/src/db/db/dbShapeProcessor.cc index 95873cc27..0d0ba329f 100644 --- a/src/db/db/dbShapeProcessor.cc +++ b/src/db/db/dbShapeProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShapeProcessor.h b/src/db/db/dbShapeProcessor.h index f8522ffd2..631fdf02b 100644 --- a/src/db/db/dbShapeProcessor.h +++ b/src/db/db/dbShapeProcessor.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShapeRepository.h b/src/db/db/dbShapeRepository.h index 19db40d96..fd5c67a5f 100644 --- a/src/db/db/dbShapeRepository.h +++ b/src/db/db/dbShapeRepository.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShapes.cc b/src/db/db/dbShapes.cc index 37f21b25e..4dd7d1341 100644 --- a/src/db/db/dbShapes.cc +++ b/src/db/db/dbShapes.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShapes.h b/src/db/db/dbShapes.h index 94d87b7bb..bc5100f8f 100644 --- a/src/db/db/dbShapes.h +++ b/src/db/db/dbShapes.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShapes2.cc b/src/db/db/dbShapes2.cc index cd545ee98..e8b82e555 100644 --- a/src/db/db/dbShapes2.cc +++ b/src/db/db/dbShapes2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShapes2.h b/src/db/db/dbShapes2.h index 2bf33a88a..abb084086 100644 --- a/src/db/db/dbShapes2.h +++ b/src/db/db/dbShapes2.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbShapes3.cc b/src/db/db/dbShapes3.cc index 0f5737665..099bb3702 100644 --- a/src/db/db/dbShapes3.cc +++ b/src/db/db/dbShapes3.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbStatic.cc b/src/db/db/dbStatic.cc index f4e9e7f66..71869a64a 100644 --- a/src/db/db/dbStatic.cc +++ b/src/db/db/dbStatic.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbStatic.h b/src/db/db/dbStatic.h index f4c2c7672..e64c92f1f 100644 --- a/src/db/db/dbStatic.h +++ b/src/db/db/dbStatic.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbStream.cc b/src/db/db/dbStream.cc index a6b84c9aa..938bd9a60 100644 --- a/src/db/db/dbStream.cc +++ b/src/db/db/dbStream.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbStream.h b/src/db/db/dbStream.h index 0bc623dc8..222701726 100644 --- a/src/db/db/dbStream.h +++ b/src/db/db/dbStream.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbStreamLayers.cc b/src/db/db/dbStreamLayers.cc index 2062bc191..9a6919f61 100644 --- a/src/db/db/dbStreamLayers.cc +++ b/src/db/db/dbStreamLayers.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbStreamLayers.h b/src/db/db/dbStreamLayers.h index 707364629..9ea8d90f1 100644 --- a/src/db/db/dbStreamLayers.h +++ b/src/db/db/dbStreamLayers.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTechnology.cc b/src/db/db/dbTechnology.cc index bb9bac46c..a4acd4ce0 100644 --- a/src/db/db/dbTechnology.cc +++ b/src/db/db/dbTechnology.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTechnology.h b/src/db/db/dbTechnology.h index fd4ac3a02..f2acec09e 100644 --- a/src/db/db/dbTechnology.h +++ b/src/db/db/dbTechnology.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index f8d7516e0..3f7798bef 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index 82be9cf34..00fb38432 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbText.cc b/src/db/db/dbText.cc index 92b83e90c..7dbddcf13 100644 --- a/src/db/db/dbText.cc +++ b/src/db/db/dbText.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbText.h b/src/db/db/dbText.h index fe9ce4de0..e80d39662 100644 --- a/src/db/db/dbText.h +++ b/src/db/db/dbText.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTextWriter.cc b/src/db/db/dbTextWriter.cc index babe9f4e0..4ceabb7ae 100644 --- a/src/db/db/dbTextWriter.cc +++ b/src/db/db/dbTextWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTextWriter.h b/src/db/db/dbTextWriter.h index 391ffd99f..fbcfd5814 100644 --- a/src/db/db/dbTextWriter.h +++ b/src/db/db/dbTextWriter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTilingProcessor.cc b/src/db/db/dbTilingProcessor.cc index f061f769a..ac4439914 100644 --- a/src/db/db/dbTilingProcessor.cc +++ b/src/db/db/dbTilingProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTilingProcessor.h b/src/db/db/dbTilingProcessor.h index 9ac8dcb57..ed492c07c 100644 --- a/src/db/db/dbTilingProcessor.h +++ b/src/db/db/dbTilingProcessor.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTrans.cc b/src/db/db/dbTrans.cc index 6df23d32e..5fecdae5c 100644 --- a/src/db/db/dbTrans.cc +++ b/src/db/db/dbTrans.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTrans.h b/src/db/db/dbTrans.h index f64bb3b98..edd541894 100644 --- a/src/db/db/dbTrans.h +++ b/src/db/db/dbTrans.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbTypes.h b/src/db/db/dbTypes.h index 11df2b7a9..764e3b6bc 100644 --- a/src/db/db/dbTypes.h +++ b/src/db/db/dbTypes.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbUserObject.cc b/src/db/db/dbUserObject.cc index 09297e2a0..819efd955 100644 --- a/src/db/db/dbUserObject.cc +++ b/src/db/db/dbUserObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbUserObject.h b/src/db/db/dbUserObject.h index 4c4eac41c..c3a4bbec7 100644 --- a/src/db/db/dbUserObject.h +++ b/src/db/db/dbUserObject.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbVariableWidthPath.cc b/src/db/db/dbVariableWidthPath.cc index 348aa5c25..fd6546418 100644 --- a/src/db/db/dbVariableWidthPath.cc +++ b/src/db/db/dbVariableWidthPath.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbVariableWidthPath.h b/src/db/db/dbVariableWidthPath.h index a87c7aca6..39984a963 100644 --- a/src/db/db/dbVariableWidthPath.h +++ b/src/db/db/dbVariableWidthPath.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbVector.cc b/src/db/db/dbVector.cc index db43023fb..da241ec04 100644 --- a/src/db/db/dbVector.cc +++ b/src/db/db/dbVector.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbVector.h b/src/db/db/dbVector.h index f3432c057..0d81a11a0 100644 --- a/src/db/db/dbVector.h +++ b/src/db/db/dbVector.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbWriter.cc b/src/db/db/dbWriter.cc index a133b5fa8..fee62e923 100644 --- a/src/db/db/dbWriter.cc +++ b/src/db/db/dbWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbWriter.h b/src/db/db/dbWriter.h index 06b29d6b0..9ccadbff0 100644 --- a/src/db/db/dbWriter.h +++ b/src/db/db/dbWriter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbWriterTools.cc b/src/db/db/dbWriterTools.cc index acb714a88..2d685944d 100644 --- a/src/db/db/dbWriterTools.cc +++ b/src/db/db/dbWriterTools.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/dbWriterTools.h b/src/db/db/dbWriterTools.h index 983f5cdd7..2c3168245 100644 --- a/src/db/db/dbWriterTools.h +++ b/src/db/db/dbWriterTools.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbBox.cc b/src/db/db/gsiDeclDbBox.cc index 0ee29b258..f28af36e7 100644 --- a/src/db/db/gsiDeclDbBox.cc +++ b/src/db/db/gsiDeclDbBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 220fd40f8..5025c7a21 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbCellMapping.cc b/src/db/db/gsiDeclDbCellMapping.cc index 82454184e..3c9dd9552 100644 --- a/src/db/db/gsiDeclDbCellMapping.cc +++ b/src/db/db/gsiDeclDbCellMapping.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbCommonStreamOptions.cc b/src/db/db/gsiDeclDbCommonStreamOptions.cc index 53ab34891..ad56d81cd 100644 --- a/src/db/db/gsiDeclDbCommonStreamOptions.cc +++ b/src/db/db/gsiDeclDbCommonStreamOptions.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbEdge.cc b/src/db/db/gsiDeclDbEdge.cc index f60e70dca..4cf150859 100644 --- a/src/db/db/gsiDeclDbEdge.cc +++ b/src/db/db/gsiDeclDbEdge.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbEdgePair.cc b/src/db/db/gsiDeclDbEdgePair.cc index a7b35c4fa..17e763dba 100644 --- a/src/db/db/gsiDeclDbEdgePair.cc +++ b/src/db/db/gsiDeclDbEdgePair.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index b8277d2e3..7c940734c 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbEdgeProcessor.cc b/src/db/db/gsiDeclDbEdgeProcessor.cc index 3352a8eb9..0ec50f08f 100644 --- a/src/db/db/gsiDeclDbEdgeProcessor.cc +++ b/src/db/db/gsiDeclDbEdgeProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 9166a6f68..24e0851c1 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbGlyphs.cc b/src/db/db/gsiDeclDbGlyphs.cc index c077e0eb5..0a6d6989d 100644 --- a/src/db/db/gsiDeclDbGlyphs.cc +++ b/src/db/db/gsiDeclDbGlyphs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbHierNetworkProcessor.cc b/src/db/db/gsiDeclDbHierNetworkProcessor.cc index d9c794a0a..a72f15d9c 100644 --- a/src/db/db/gsiDeclDbHierNetworkProcessor.cc +++ b/src/db/db/gsiDeclDbHierNetworkProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbInstElement.cc b/src/db/db/gsiDeclDbInstElement.cc index 137400574..ef979cbeb 100644 --- a/src/db/db/gsiDeclDbInstElement.cc +++ b/src/db/db/gsiDeclDbInstElement.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbLayerMapping.cc b/src/db/db/gsiDeclDbLayerMapping.cc index a960b9850..2d75b5f70 100644 --- a/src/db/db/gsiDeclDbLayerMapping.cc +++ b/src/db/db/gsiDeclDbLayerMapping.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 537cd5ada..4436ed79d 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbLayoutDiff.cc b/src/db/db/gsiDeclDbLayoutDiff.cc index 5b515f139..f2f31af99 100644 --- a/src/db/db/gsiDeclDbLayoutDiff.cc +++ b/src/db/db/gsiDeclDbLayoutDiff.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbLayoutQuery.cc b/src/db/db/gsiDeclDbLayoutQuery.cc index c5a0e6315..364f349d8 100644 --- a/src/db/db/gsiDeclDbLayoutQuery.cc +++ b/src/db/db/gsiDeclDbLayoutQuery.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 6b7d4ea0d..ad0539767 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbLayoutUtils.cc b/src/db/db/gsiDeclDbLayoutUtils.cc index b44011315..1b7f21cf1 100644 --- a/src/db/db/gsiDeclDbLayoutUtils.cc +++ b/src/db/db/gsiDeclDbLayoutUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index 7f9e7965c..35a9144cd 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbManager.cc b/src/db/db/gsiDeclDbManager.cc index ff41107d7..2bd53bfe4 100644 --- a/src/db/db/gsiDeclDbManager.cc +++ b/src/db/db/gsiDeclDbManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbMatrix.cc b/src/db/db/gsiDeclDbMatrix.cc index 659f24251..0e35bf6a2 100644 --- a/src/db/db/gsiDeclDbMatrix.cc +++ b/src/db/db/gsiDeclDbMatrix.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 6f87c25ac..3d0bf45e7 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbNetlistDeviceClasses.cc b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc index e78bfd3bd..8d1fc2ac4 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceClasses.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index 0c8e281c8..a201164a8 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbPath.cc b/src/db/db/gsiDeclDbPath.cc index 1d8ebd0ba..6ec8b2d6e 100644 --- a/src/db/db/gsiDeclDbPath.cc +++ b/src/db/db/gsiDeclDbPath.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbPoint.cc b/src/db/db/gsiDeclDbPoint.cc index 490b23773..5c484d6ad 100644 --- a/src/db/db/gsiDeclDbPoint.cc +++ b/src/db/db/gsiDeclDbPoint.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index bb4d68a31..9e2856b55 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbReader.cc b/src/db/db/gsiDeclDbReader.cc index 65d62ee2d..52438749c 100644 --- a/src/db/db/gsiDeclDbReader.cc +++ b/src/db/db/gsiDeclDbReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index 566d02121..5f232a871 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 6aa47528a..9d7d653d0 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc index 35aa60cff..eb9aa128f 100644 --- a/src/db/db/gsiDeclDbShape.cc +++ b/src/db/db/gsiDeclDbShape.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbShapeProcessor.cc b/src/db/db/gsiDeclDbShapeProcessor.cc index b9b36f3a8..6fe7dedb5 100644 --- a/src/db/db/gsiDeclDbShapeProcessor.cc +++ b/src/db/db/gsiDeclDbShapeProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbShapes.cc b/src/db/db/gsiDeclDbShapes.cc index 27653f9b3..08d79b7b0 100644 --- a/src/db/db/gsiDeclDbShapes.cc +++ b/src/db/db/gsiDeclDbShapes.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbTechnologies.cc b/src/db/db/gsiDeclDbTechnologies.cc index c4a54c7ee..a64378c85 100644 --- a/src/db/db/gsiDeclDbTechnologies.cc +++ b/src/db/db/gsiDeclDbTechnologies.cc @@ -1,7 +1,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbText.cc b/src/db/db/gsiDeclDbText.cc index ce384492d..2d7630068 100644 --- a/src/db/db/gsiDeclDbText.cc +++ b/src/db/db/gsiDeclDbText.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbTilingProcessor.cc b/src/db/db/gsiDeclDbTilingProcessor.cc index 56a99b177..ea142a971 100644 --- a/src/db/db/gsiDeclDbTilingProcessor.cc +++ b/src/db/db/gsiDeclDbTilingProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbTrans.cc b/src/db/db/gsiDeclDbTrans.cc index 43a981241..8216c00f8 100644 --- a/src/db/db/gsiDeclDbTrans.cc +++ b/src/db/db/gsiDeclDbTrans.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/db/gsiDeclDbVector.cc b/src/db/db/gsiDeclDbVector.cc index eca3c8f85..21182dabb 100644 --- a/src/db/db/gsiDeclDbVector.cc +++ b/src/db/db/gsiDeclDbVector.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbArray.cc b/src/db/unit_tests/dbArray.cc index 976752ee2..524369002 100644 --- a/src/db/unit_tests/dbArray.cc +++ b/src/db/unit_tests/dbArray.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbBox.cc b/src/db/unit_tests/dbBox.cc index 5a3396c7b..e48084a42 100644 --- a/src/db/unit_tests/dbBox.cc +++ b/src/db/unit_tests/dbBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbBoxScanner.cc b/src/db/unit_tests/dbBoxScanner.cc index 5e3e1e591..db4b660d2 100644 --- a/src/db/unit_tests/dbBoxScanner.cc +++ b/src/db/unit_tests/dbBoxScanner.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbBoxTree.cc b/src/db/unit_tests/dbBoxTree.cc index e9663db14..a5966efe5 100644 --- a/src/db/unit_tests/dbBoxTree.cc +++ b/src/db/unit_tests/dbBoxTree.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbCell.cc b/src/db/unit_tests/dbCell.cc index 1e2ef168e..29ec740fa 100644 --- a/src/db/unit_tests/dbCell.cc +++ b/src/db/unit_tests/dbCell.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbCellGraphUtils.cc b/src/db/unit_tests/dbCellGraphUtils.cc index 77bf27adb..bb433fca2 100644 --- a/src/db/unit_tests/dbCellGraphUtils.cc +++ b/src/db/unit_tests/dbCellGraphUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbCellHullGenerator.cc b/src/db/unit_tests/dbCellHullGenerator.cc index 1ca068229..b2ceb23b3 100644 --- a/src/db/unit_tests/dbCellHullGenerator.cc +++ b/src/db/unit_tests/dbCellHullGenerator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbCellMapping.cc b/src/db/unit_tests/dbCellMapping.cc index 0d346e0a6..f493f6116 100644 --- a/src/db/unit_tests/dbCellMapping.cc +++ b/src/db/unit_tests/dbCellMapping.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbClip.cc b/src/db/unit_tests/dbClip.cc index aab1affb2..5cbaf44d6 100644 --- a/src/db/unit_tests/dbClip.cc +++ b/src/db/unit_tests/dbClip.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 863566678..720708876 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc index 1eb8371d5..982548adb 100644 --- a/src/db/unit_tests/dbDeepShapeStoreTests.cc +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbEdge.cc b/src/db/unit_tests/dbEdge.cc index 32d91641b..940e7a474 100644 --- a/src/db/unit_tests/dbEdge.cc +++ b/src/db/unit_tests/dbEdge.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbEdgePair.cc b/src/db/unit_tests/dbEdgePair.cc index 3b8f19b65..732bd592c 100644 --- a/src/db/unit_tests/dbEdgePair.cc +++ b/src/db/unit_tests/dbEdgePair.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbEdgePairRelations.cc b/src/db/unit_tests/dbEdgePairRelations.cc index 69330f6f1..2097e08ae 100644 --- a/src/db/unit_tests/dbEdgePairRelations.cc +++ b/src/db/unit_tests/dbEdgePairRelations.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbEdgePairs.cc b/src/db/unit_tests/dbEdgePairs.cc index 743899515..6ac6e5f77 100644 --- a/src/db/unit_tests/dbEdgePairs.cc +++ b/src/db/unit_tests/dbEdgePairs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbEdgeProcessor.cc b/src/db/unit_tests/dbEdgeProcessor.cc index 5c019c9be..4bd47bb77 100644 --- a/src/db/unit_tests/dbEdgeProcessor.cc +++ b/src/db/unit_tests/dbEdgeProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbEdges.cc b/src/db/unit_tests/dbEdges.cc index d37e10a31..eea55da20 100644 --- a/src/db/unit_tests/dbEdges.cc +++ b/src/db/unit_tests/dbEdges.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbEdgesToContours.cc b/src/db/unit_tests/dbEdgesToContours.cc index 0d27a812e..921ee8d49 100644 --- a/src/db/unit_tests/dbEdgesToContours.cc +++ b/src/db/unit_tests/dbEdgesToContours.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbExpression.cc b/src/db/unit_tests/dbExpression.cc index 6ea2938a0..6f051968b 100644 --- a/src/db/unit_tests/dbExpression.cc +++ b/src/db/unit_tests/dbExpression.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 06798f741..337780a04 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 541b23f00..a457db90d 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc index 54bc333d0..3892eecf4 100644 --- a/src/db/unit_tests/dbHierarchyBuilderTests.cc +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbLayer.cc b/src/db/unit_tests/dbLayer.cc index e0443ed00..70797462b 100644 --- a/src/db/unit_tests/dbLayer.cc +++ b/src/db/unit_tests/dbLayer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbLayerMapping.cc b/src/db/unit_tests/dbLayerMapping.cc index 810df062c..41b25a39e 100644 --- a/src/db/unit_tests/dbLayerMapping.cc +++ b/src/db/unit_tests/dbLayerMapping.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbLayout.cc b/src/db/unit_tests/dbLayout.cc index bb89b4511..37f6b0de1 100644 --- a/src/db/unit_tests/dbLayout.cc +++ b/src/db/unit_tests/dbLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbLayoutDiff.cc b/src/db/unit_tests/dbLayoutDiff.cc index 964397834..9ed39d42c 100644 --- a/src/db/unit_tests/dbLayoutDiff.cc +++ b/src/db/unit_tests/dbLayoutDiff.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbLayoutQuery.cc b/src/db/unit_tests/dbLayoutQuery.cc index 965faf4ac..7033ed379 100644 --- a/src/db/unit_tests/dbLayoutQuery.cc +++ b/src/db/unit_tests/dbLayoutQuery.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 85aa1c411..91ccde1fe 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbLayoutUtils.cc b/src/db/unit_tests/dbLayoutUtils.cc index 39f7a5730..57b73bb94 100644 --- a/src/db/unit_tests/dbLayoutUtils.cc +++ b/src/db/unit_tests/dbLayoutUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbLibraries.cc b/src/db/unit_tests/dbLibraries.cc index d304fce25..b92b19779 100644 --- a/src/db/unit_tests/dbLibraries.cc +++ b/src/db/unit_tests/dbLibraries.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbLoadLayoutOptionsTests.cc b/src/db/unit_tests/dbLoadLayoutOptionsTests.cc index 2e59b9f1e..17433b3e9 100644 --- a/src/db/unit_tests/dbLoadLayoutOptionsTests.cc +++ b/src/db/unit_tests/dbLoadLayoutOptionsTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbMatrix.cc b/src/db/unit_tests/dbMatrix.cc index 50fe356fd..b3f88690b 100644 --- a/src/db/unit_tests/dbMatrix.cc +++ b/src/db/unit_tests/dbMatrix.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc index 37e346e84..f37a5e397 100644 --- a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index a11c14d49..3cba2aaf5 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 3dd6e3630..36c1d1ffd 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbNetlistPropertyTests.cc b/src/db/unit_tests/dbNetlistPropertyTests.cc index e477d15e5..bbac12a0c 100644 --- a/src/db/unit_tests/dbNetlistPropertyTests.cc +++ b/src/db/unit_tests/dbNetlistPropertyTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 07682ab06..833f42f95 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbObject.cc b/src/db/unit_tests/dbObject.cc index e341554d8..036bee571 100644 --- a/src/db/unit_tests/dbObject.cc +++ b/src/db/unit_tests/dbObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbPCells.cc b/src/db/unit_tests/dbPCells.cc index a50ded9a1..576130741 100644 --- a/src/db/unit_tests/dbPCells.cc +++ b/src/db/unit_tests/dbPCells.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbPath.cc b/src/db/unit_tests/dbPath.cc index b9688b992..be4a76e4b 100644 --- a/src/db/unit_tests/dbPath.cc +++ b/src/db/unit_tests/dbPath.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbPoint.cc b/src/db/unit_tests/dbPoint.cc index 18cc37eb5..8bab93227 100644 --- a/src/db/unit_tests/dbPoint.cc +++ b/src/db/unit_tests/dbPoint.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbPolygon.cc b/src/db/unit_tests/dbPolygon.cc index 782035ed9..b9c03fe41 100644 --- a/src/db/unit_tests/dbPolygon.cc +++ b/src/db/unit_tests/dbPolygon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbPolygonTools.cc b/src/db/unit_tests/dbPolygonTools.cc index 6229f89f4..5e6d4fb9b 100644 --- a/src/db/unit_tests/dbPolygonTools.cc +++ b/src/db/unit_tests/dbPolygonTools.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbPropertiesRepository.cc b/src/db/unit_tests/dbPropertiesRepository.cc index d5b3c706d..31ab536f3 100644 --- a/src/db/unit_tests/dbPropertiesRepository.cc +++ b/src/db/unit_tests/dbPropertiesRepository.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 67ab0f993..bbf1f171c 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbRegion.cc b/src/db/unit_tests/dbRegion.cc index aca7b2640..6be478378 100644 --- a/src/db/unit_tests/dbRegion.cc +++ b/src/db/unit_tests/dbRegion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbSaveLayoutOptionsTests.cc b/src/db/unit_tests/dbSaveLayoutOptionsTests.cc index d8ecee18b..832ca7060 100644 --- a/src/db/unit_tests/dbSaveLayoutOptionsTests.cc +++ b/src/db/unit_tests/dbSaveLayoutOptionsTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbShape.cc b/src/db/unit_tests/dbShape.cc index 5f27d10ba..eebb47981 100644 --- a/src/db/unit_tests/dbShape.cc +++ b/src/db/unit_tests/dbShape.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbShapeArray.cc b/src/db/unit_tests/dbShapeArray.cc index 71cbd83f4..558ec92f5 100644 --- a/src/db/unit_tests/dbShapeArray.cc +++ b/src/db/unit_tests/dbShapeArray.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbShapeRepository.cc b/src/db/unit_tests/dbShapeRepository.cc index 35b168570..feb8a1bdd 100644 --- a/src/db/unit_tests/dbShapeRepository.cc +++ b/src/db/unit_tests/dbShapeRepository.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbShapes.cc b/src/db/unit_tests/dbShapes.cc index f7c3af9f1..5d16390d2 100644 --- a/src/db/unit_tests/dbShapes.cc +++ b/src/db/unit_tests/dbShapes.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbStreamLayers.cc b/src/db/unit_tests/dbStreamLayers.cc index 35ff01783..3fbe21d39 100644 --- a/src/db/unit_tests/dbStreamLayers.cc +++ b/src/db/unit_tests/dbStreamLayers.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbText.cc b/src/db/unit_tests/dbText.cc index 613e2a3ef..237b85888 100644 --- a/src/db/unit_tests/dbText.cc +++ b/src/db/unit_tests/dbText.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbTilingProcessor.cc b/src/db/unit_tests/dbTilingProcessor.cc index 862461b58..ae47fb25a 100644 --- a/src/db/unit_tests/dbTilingProcessor.cc +++ b/src/db/unit_tests/dbTilingProcessor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbTrans.cc b/src/db/unit_tests/dbTrans.cc index 839b06aad..73c5468ad 100644 --- a/src/db/unit_tests/dbTrans.cc +++ b/src/db/unit_tests/dbTrans.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbVariableWidthPath.cc b/src/db/unit_tests/dbVariableWidthPath.cc index 7c0abf178..37e9416a7 100644 --- a/src/db/unit_tests/dbVariableWidthPath.cc +++ b/src/db/unit_tests/dbVariableWidthPath.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbVector.cc b/src/db/unit_tests/dbVector.cc index e4fa3c39d..e62196836 100644 --- a/src/db/unit_tests/dbVector.cc +++ b/src/db/unit_tests/dbVector.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/db/unit_tests/dbWriterTools.cc b/src/db/unit_tests/dbWriterTools.cc index 304f706ec..064d4a9ce 100644 --- a/src/db/unit_tests/dbWriterTools.cc +++ b/src/db/unit_tests/dbWriterTools.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/drc/drc/drcCommon.h b/src/drc/drc/drcCommon.h index f03c7437b..6c828be66 100644 --- a/src/drc/drc/drcCommon.h +++ b/src/drc/drc/drcCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/drc/drc/drcForceLink.cc b/src/drc/drc/drcForceLink.cc index fc22b136b..4f4dbc3ae 100644 --- a/src/drc/drc/drcForceLink.cc +++ b/src/drc/drc/drcForceLink.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/drc/drc/drcForceLink.h b/src/drc/drc/drcForceLink.h index cb365bde0..6f06efbc7 100644 --- a/src/drc/drc/drcForceLink.h +++ b/src/drc/drc/drcForceLink.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/drc/unit_tests/drcBasicTests.cc b/src/drc/unit_tests/drcBasicTests.cc index 76de7cad9..a5978b6d6 100644 --- a/src/drc/unit_tests/drcBasicTests.cc +++ b/src/drc/unit_tests/drcBasicTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 46d101643..b3d225ce8 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/drc/unit_tests/drcSuiteTests.cc b/src/drc/unit_tests/drcSuiteTests.cc index fe4ed0556..94e043626 100644 --- a/src/drc/unit_tests/drcSuiteTests.cc +++ b/src/drc/unit_tests/drcSuiteTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtCommon.h b/src/edt/edt/edtCommon.h index 58c7eabd1..959aa091a 100644 --- a/src/edt/edt/edtCommon.h +++ b/src/edt/edt/edtCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtConfig.cc b/src/edt/edt/edtConfig.cc index f559a1331..2c62dac78 100644 --- a/src/edt/edt/edtConfig.cc +++ b/src/edt/edt/edtConfig.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtConfig.h b/src/edt/edt/edtConfig.h index ec7cb4677..b727ea4a7 100644 --- a/src/edt/edt/edtConfig.h +++ b/src/edt/edt/edtConfig.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtDialogs.cc b/src/edt/edt/edtDialogs.cc index 5597dc517..9755f19d9 100644 --- a/src/edt/edt/edtDialogs.cc +++ b/src/edt/edt/edtDialogs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtDialogs.h b/src/edt/edt/edtDialogs.h index 7dc31e121..9fc17919b 100644 --- a/src/edt/edt/edtDialogs.h +++ b/src/edt/edt/edtDialogs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index b5eba00e6..4114ff04d 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 18e8d9ee0..058a48213 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index 6bf78393d..12ca5280c 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtInstPropertiesPage.h b/src/edt/edt/edtInstPropertiesPage.h index 117736775..014a645cf 100644 --- a/src/edt/edt/edtInstPropertiesPage.h +++ b/src/edt/edt/edtInstPropertiesPage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index ce4f05a41..fc848c3b3 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index 8e7b92196..5f32f493f 100644 --- a/src/edt/edt/edtMainService.h +++ b/src/edt/edt/edtMainService.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index cbd5fe417..b39a35d88 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index 7235537c1..e55938b6e 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 458e9da53..2de47a90d 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtPartialService.h b/src/edt/edt/edtPartialService.h index f38a1bb71..495bae649 100644 --- a/src/edt/edt/edtPartialService.h +++ b/src/edt/edt/edtPartialService.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 607053e88..d97395acf 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtPlugin.h b/src/edt/edt/edtPlugin.h index 5c7263e3e..27daa94c5 100644 --- a/src/edt/edt/edtPlugin.h +++ b/src/edt/edt/edtPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtPropertiesPageUtils.cc b/src/edt/edt/edtPropertiesPageUtils.cc index ea46d5c6d..e9b33c1ba 100644 --- a/src/edt/edt/edtPropertiesPageUtils.cc +++ b/src/edt/edt/edtPropertiesPageUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtPropertiesPageUtils.h b/src/edt/edt/edtPropertiesPageUtils.h index 729ea0c83..0dc885a93 100644 --- a/src/edt/edt/edtPropertiesPageUtils.h +++ b/src/edt/edt/edtPropertiesPageUtils.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtPropertiesPages.cc b/src/edt/edt/edtPropertiesPages.cc index 0ad5dea9f..8c71c113e 100644 --- a/src/edt/edt/edtPropertiesPages.cc +++ b/src/edt/edt/edtPropertiesPages.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtPropertiesPages.h b/src/edt/edt/edtPropertiesPages.h index 00e9d66f3..5fbadf5eb 100644 --- a/src/edt/edt/edtPropertiesPages.h +++ b/src/edt/edt/edtPropertiesPages.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 554073692..e01d98187 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index ba0855b08..4987d8d8e 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 208a72c16..f8ab4eac6 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 57234d05c..6a1b05e1e 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtUtils.cc b/src/edt/edt/edtUtils.cc index 3aa82127c..40d8d7014 100644 --- a/src/edt/edt/edtUtils.cc +++ b/src/edt/edt/edtUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/edtUtils.h b/src/edt/edt/edtUtils.h index 68b9165b2..f8501cee4 100644 --- a/src/edt/edt/edtUtils.h +++ b/src/edt/edt/edtUtils.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/edt/gsiDeclEdt.cc b/src/edt/edt/gsiDeclEdt.cc index 75d558ba8..0b69c807f 100644 --- a/src/edt/edt/gsiDeclEdt.cc +++ b/src/edt/edt/gsiDeclEdt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/edt/unit_tests/edtBasicTests.cc b/src/edt/unit_tests/edtBasicTests.cc index 5e9eff146..9a8110da1 100644 --- a/src/edt/unit_tests/edtBasicTests.cc +++ b/src/edt/unit_tests/edtBasicTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/fontgen/fontgen.cc b/src/fontgen/fontgen.cc index 557ac6c64..289abfc94 100644 --- a/src/fontgen/fontgen.cc +++ b/src/fontgen/fontgen.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsi.cc b/src/gsi/gsi/gsi.cc index 440657f84..bee9b1f8f 100644 --- a/src/gsi/gsi/gsi.cc +++ b/src/gsi/gsi/gsi.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsi.h b/src/gsi/gsi/gsi.h index 75113791d..3bf5dc0b9 100644 --- a/src/gsi/gsi/gsi.h +++ b/src/gsi/gsi/gsi.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiCallback.h b/src/gsi/gsi/gsiCallback.h index e37621823..bb5814426 100644 --- a/src/gsi/gsi/gsiCallback.h +++ b/src/gsi/gsi/gsiCallback.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiCallbackVar.h b/src/gsi/gsi/gsiCallbackVar.h index 7289cfd17..e2681bdef 100644 --- a/src/gsi/gsi/gsiCallbackVar.h +++ b/src/gsi/gsi/gsiCallbackVar.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiClass.cc b/src/gsi/gsi/gsiClass.cc index 17c9053c0..a6fc5cb39 100644 --- a/src/gsi/gsi/gsiClass.cc +++ b/src/gsi/gsi/gsiClass.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiClass.h b/src/gsi/gsi/gsiClass.h index b51944c4d..7712b028f 100644 --- a/src/gsi/gsi/gsiClass.h +++ b/src/gsi/gsi/gsiClass.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiClassBase.cc b/src/gsi/gsi/gsiClassBase.cc index 70e2d7f73..e6548ef8f 100644 --- a/src/gsi/gsi/gsiClassBase.cc +++ b/src/gsi/gsi/gsiClassBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiClassBase.h b/src/gsi/gsi/gsiClassBase.h index 8716e7de0..a785c4919 100644 --- a/src/gsi/gsi/gsiClassBase.h +++ b/src/gsi/gsi/gsiClassBase.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiCommon.h b/src/gsi/gsi/gsiCommon.h index caf7eb4d1..b0bb7b5a3 100644 --- a/src/gsi/gsi/gsiCommon.h +++ b/src/gsi/gsi/gsiCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiDecl.h b/src/gsi/gsi/gsiDecl.h index d7698dd17..625000383 100644 --- a/src/gsi/gsi/gsiDecl.h +++ b/src/gsi/gsi/gsiDecl.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiDeclBasic.cc b/src/gsi/gsi/gsiDeclBasic.cc index f434b2eb2..e035e39be 100644 --- a/src/gsi/gsi/gsiDeclBasic.cc +++ b/src/gsi/gsi/gsiDeclBasic.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiDeclBasic.h b/src/gsi/gsi/gsiDeclBasic.h index 57d2b8dec..7a3bf1270 100644 --- a/src/gsi/gsi/gsiDeclBasic.h +++ b/src/gsi/gsi/gsiDeclBasic.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiDeclInternal.cc b/src/gsi/gsi/gsiDeclInternal.cc index e8bd7022a..333ce940c 100644 --- a/src/gsi/gsi/gsiDeclInternal.cc +++ b/src/gsi/gsi/gsiDeclInternal.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index dfcee4871..7c4dc3a5b 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiEnums.h b/src/gsi/gsi/gsiEnums.h index cbddb56a3..e7f7a2c8d 100644 --- a/src/gsi/gsi/gsiEnums.h +++ b/src/gsi/gsi/gsiEnums.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index 53c55f372..5fa8e1791 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiExpression.h b/src/gsi/gsi/gsiExpression.h index 22bec5d83..c50145382 100644 --- a/src/gsi/gsi/gsiExpression.h +++ b/src/gsi/gsi/gsiExpression.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiExternalMain.cc b/src/gsi/gsi/gsiExternalMain.cc index b8e95f044..e8cc56ae9 100644 --- a/src/gsi/gsi/gsiExternalMain.cc +++ b/src/gsi/gsi/gsiExternalMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiExternalMain.h b/src/gsi/gsi/gsiExternalMain.h index 088678b09..b826c76d2 100644 --- a/src/gsi/gsi/gsiExternalMain.h +++ b/src/gsi/gsi/gsiExternalMain.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiInspector.cc b/src/gsi/gsi/gsiInspector.cc index 14e5bf6f9..ae16609b9 100644 --- a/src/gsi/gsi/gsiInspector.cc +++ b/src/gsi/gsi/gsiInspector.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiInspector.h b/src/gsi/gsi/gsiInspector.h index 6f33219d2..befc0aab4 100644 --- a/src/gsi/gsi/gsiInspector.h +++ b/src/gsi/gsi/gsiInspector.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiInterpreter.cc b/src/gsi/gsi/gsiInterpreter.cc index a750690dc..9ef7f0756 100644 --- a/src/gsi/gsi/gsiInterpreter.cc +++ b/src/gsi/gsi/gsiInterpreter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiInterpreter.h b/src/gsi/gsi/gsiInterpreter.h index 186b6191e..adc05276e 100644 --- a/src/gsi/gsi/gsiInterpreter.h +++ b/src/gsi/gsi/gsiInterpreter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiIterators.h b/src/gsi/gsi/gsiIterators.h index 195003ee4..7805b86c3 100644 --- a/src/gsi/gsi/gsiIterators.h +++ b/src/gsi/gsi/gsiIterators.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiMethods.cc b/src/gsi/gsi/gsiMethods.cc index c8a4f21a5..979aeca4f 100644 --- a/src/gsi/gsi/gsiMethods.cc +++ b/src/gsi/gsi/gsiMethods.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiMethods.h b/src/gsi/gsi/gsiMethods.h index d280395c1..472748710 100644 --- a/src/gsi/gsi/gsiMethods.h +++ b/src/gsi/gsi/gsiMethods.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiMethodsVar.h b/src/gsi/gsi/gsiMethodsVar.h index 4cc2e1877..db383beaf 100644 --- a/src/gsi/gsi/gsiMethodsVar.h +++ b/src/gsi/gsi/gsiMethodsVar.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiObject.cc b/src/gsi/gsi/gsiObject.cc index 7a277dc15..29f5c021e 100644 --- a/src/gsi/gsi/gsiObject.cc +++ b/src/gsi/gsi/gsiObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiObject.h b/src/gsi/gsi/gsiObject.h index bdde6d7d7..e59a41791 100644 --- a/src/gsi/gsi/gsiObject.h +++ b/src/gsi/gsi/gsiObject.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiObjectHolder.cc b/src/gsi/gsi/gsiObjectHolder.cc index 5d85a5924..5b406721a 100644 --- a/src/gsi/gsi/gsiObjectHolder.cc +++ b/src/gsi/gsi/gsiObjectHolder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiObjectHolder.h b/src/gsi/gsi/gsiObjectHolder.h index 5d98e5a1a..3ab8ae3da 100644 --- a/src/gsi/gsi/gsiObjectHolder.h +++ b/src/gsi/gsi/gsiObjectHolder.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiSerialisation.cc b/src/gsi/gsi/gsiSerialisation.cc index 2cce88134..e9fa3ff98 100644 --- a/src/gsi/gsi/gsiSerialisation.cc +++ b/src/gsi/gsi/gsiSerialisation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiSerialisation.h b/src/gsi/gsi/gsiSerialisation.h index 675c9b0a3..670e5638f 100644 --- a/src/gsi/gsi/gsiSerialisation.h +++ b/src/gsi/gsi/gsiSerialisation.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiSignals.cc b/src/gsi/gsi/gsiSignals.cc index fcd24adcd..4a3fa8af2 100644 --- a/src/gsi/gsi/gsiSignals.cc +++ b/src/gsi/gsi/gsiSignals.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiSignals.h b/src/gsi/gsi/gsiSignals.h index 9bd43be7e..773d3d270 100644 --- a/src/gsi/gsi/gsiSignals.h +++ b/src/gsi/gsi/gsiSignals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiTypes.cc b/src/gsi/gsi/gsiTypes.cc index bffc48eb4..c79711b86 100644 --- a/src/gsi/gsi/gsiTypes.cc +++ b/src/gsi/gsi/gsiTypes.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi/gsiTypes.h b/src/gsi/gsi/gsiTypes.h index 578e82f46..3347772be 100644 --- a/src/gsi/gsi/gsiTypes.h +++ b/src/gsi/gsi/gsiTypes.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi_test/gsiTest.cc b/src/gsi/gsi_test/gsiTest.cc index 39a4b8119..4750e7ad3 100644 --- a/src/gsi/gsi_test/gsiTest.cc +++ b/src/gsi/gsi_test/gsiTest.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi_test/gsiTest.h b/src/gsi/gsi_test/gsiTest.h index 7e11c815d..7992a42b3 100644 --- a/src/gsi/gsi_test/gsiTest.h +++ b/src/gsi/gsi_test/gsiTest.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/gsi_test/gsiTestForceLink.h b/src/gsi/gsi_test/gsiTestForceLink.h index f2f372f9f..f817419de 100644 --- a/src/gsi/gsi_test/gsiTestForceLink.h +++ b/src/gsi/gsi_test/gsiTestForceLink.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsi/unit_tests/gsiExpression.cc b/src/gsi/unit_tests/gsiExpression.cc index 7d53ac951..8ed0b2238 100644 --- a/src/gsi/unit_tests/gsiExpression.cc +++ b/src/gsi/unit_tests/gsiExpression.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQAbstractItemModel.cc b/src/gsiqt/qt4/QtCore/gsiDeclQAbstractItemModel.cc index 2a64ceb58..a37f08506 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQAbstractItemModel.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQAbstractItemModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQAbstractListModel.cc b/src/gsiqt/qt4/QtCore/gsiDeclQAbstractListModel.cc index 790fb9394..9de52e642 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQAbstractListModel.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQAbstractListModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQAbstractTableModel.cc b/src/gsiqt/qt4/QtCore/gsiDeclQAbstractTableModel.cc index 880b9c3bf..d918e6c1c 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQAbstractTableModel.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQAbstractTableModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQBasicTimer.cc b/src/gsiqt/qt4/QtCore/gsiDeclQBasicTimer.cc index 127f5a9ec..8feb33571 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQBasicTimer.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQBasicTimer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQBuffer.cc b/src/gsiqt/qt4/QtCore/gsiDeclQBuffer.cc index a1016b113..30861edf3 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQBuffer.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQBuffer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQByteArrayMatcher.cc b/src/gsiqt/qt4/QtCore/gsiDeclQByteArrayMatcher.cc index ccc6bb207..7a9f5b12f 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQByteArrayMatcher.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQByteArrayMatcher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQChildEvent.cc b/src/gsiqt/qt4/QtCore/gsiDeclQChildEvent.cc index 9c0972648..a0e6ee68a 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQChildEvent.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQChildEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQCoreApplication.cc b/src/gsiqt/qt4/QtCore/gsiDeclQCoreApplication.cc index 1bbb59d90..048c76143 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQCoreApplication.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQCoreApplication.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQCryptographicHash.cc b/src/gsiqt/qt4/QtCore/gsiDeclQCryptographicHash.cc index a6e42854b..95c05573c 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQCryptographicHash.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQCryptographicHash.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQDataStream.cc b/src/gsiqt/qt4/QtCore/gsiDeclQDataStream.cc index 13edd65e1..b6c12e50e 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQDataStream.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQDataStream.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQDate.cc b/src/gsiqt/qt4/QtCore/gsiDeclQDate.cc index c5cb12ab3..e8e60d533 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQDate.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQDate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQDateTime.cc b/src/gsiqt/qt4/QtCore/gsiDeclQDateTime.cc index 5971223c8..e4e6f23ff 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQDateTime.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQDateTime.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQDir.cc b/src/gsiqt/qt4/QtCore/gsiDeclQDir.cc index 92fb35315..dbbdea9bf 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQDir.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQDir.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQDynamicPropertyChangeEvent.cc b/src/gsiqt/qt4/QtCore/gsiDeclQDynamicPropertyChangeEvent.cc index 70f6c6674..1e4c78bab 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQDynamicPropertyChangeEvent.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQDynamicPropertyChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQEasingCurve.cc b/src/gsiqt/qt4/QtCore/gsiDeclQEasingCurve.cc index f3f44522c..bc26377e1 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQEasingCurve.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQEasingCurve.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQEvent.cc b/src/gsiqt/qt4/QtCore/gsiDeclQEvent.cc index a3d3c285a..5e9ba697b 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQEvent.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQEventLoop.cc b/src/gsiqt/qt4/QtCore/gsiDeclQEventLoop.cc index db4148e95..ba24c736e 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQEventLoop.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQEventLoop.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQFactoryInterface.cc b/src/gsiqt/qt4/QtCore/gsiDeclQFactoryInterface.cc index 537af1bc8..326b8d6ed 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQFactoryInterface.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQFactoryInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQFile.cc b/src/gsiqt/qt4/QtCore/gsiDeclQFile.cc index e6bd8b3a4..d14114958 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQFile.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQFile.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQFileInfo.cc b/src/gsiqt/qt4/QtCore/gsiDeclQFileInfo.cc index 904758972..8bc175c32 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQFileInfo.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQFileInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQFileSystemWatcher.cc b/src/gsiqt/qt4/QtCore/gsiDeclQFileSystemWatcher.cc index 83b58e2b4..e3c370f82 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQFileSystemWatcher.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQFileSystemWatcher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQIODevice.cc b/src/gsiqt/qt4/QtCore/gsiDeclQIODevice.cc index 027febba8..6fef00817 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQIODevice.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQIODevice.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQLibrary.cc b/src/gsiqt/qt4/QtCore/gsiDeclQLibrary.cc index 897b56cb9..cf504f3a6 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQLibrary.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQLibrary.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQLibraryInfo.cc b/src/gsiqt/qt4/QtCore/gsiDeclQLibraryInfo.cc index 3a88da1c9..a24e99430 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQLibraryInfo.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQLibraryInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQLine.cc b/src/gsiqt/qt4/QtCore/gsiDeclQLine.cc index 526f4ed70..cec38e5ae 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQLine.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQLine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQLineF.cc b/src/gsiqt/qt4/QtCore/gsiDeclQLineF.cc index f70aa6483..6426b5c9d 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQLineF.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQLineF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQLocale.cc b/src/gsiqt/qt4/QtCore/gsiDeclQLocale.cc index efdda9641..365597bc4 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQLocale.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQLocale.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQMargins.cc b/src/gsiqt/qt4/QtCore/gsiDeclQMargins.cc index baa205b77..c504c8329 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQMargins.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQMargins.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQMetaClassInfo.cc b/src/gsiqt/qt4/QtCore/gsiDeclQMetaClassInfo.cc index 2dcf39d8e..1394fe529 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQMetaClassInfo.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQMetaClassInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQMetaEnum.cc b/src/gsiqt/qt4/QtCore/gsiDeclQMetaEnum.cc index 0c7154b90..26aa0773c 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQMetaEnum.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQMetaEnum.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQMetaMethod.cc b/src/gsiqt/qt4/QtCore/gsiDeclQMetaMethod.cc index 4d83cf93e..44078e954 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQMetaMethod.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQMetaMethod.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQMetaObject.cc b/src/gsiqt/qt4/QtCore/gsiDeclQMetaObject.cc index 99bbce000..ed38f3325 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQMetaObject.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQMetaObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQMetaProperty.cc b/src/gsiqt/qt4/QtCore/gsiDeclQMetaProperty.cc index e8447473a..e6f51e2b7 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQMetaProperty.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQMetaProperty.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQMetaType.cc b/src/gsiqt/qt4/QtCore/gsiDeclQMetaType.cc index 2c4af6302..27679d332 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQMetaType.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQMetaType.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQMimeData.cc b/src/gsiqt/qt4/QtCore/gsiDeclQMimeData.cc index 9406d0984..7062bce96 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQMimeData.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQMimeData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQModelIndex.cc b/src/gsiqt/qt4/QtCore/gsiDeclQModelIndex.cc index 76437adac..243c4a237 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQModelIndex.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQModelIndex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQMutex.cc b/src/gsiqt/qt4/QtCore/gsiDeclQMutex.cc index 0b08ed8cb..fbbce01cd 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQMutex.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQMutex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQObject.cc b/src/gsiqt/qt4/QtCore/gsiDeclQObject.cc index a5ef25f34..0279bf23f 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQObject.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQPersistentModelIndex.cc b/src/gsiqt/qt4/QtCore/gsiDeclQPersistentModelIndex.cc index d4a0e5f7d..b64d1e6b6 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQPersistentModelIndex.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQPersistentModelIndex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQPluginLoader.cc b/src/gsiqt/qt4/QtCore/gsiDeclQPluginLoader.cc index 15768d1ef..a6b3a8f50 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQPluginLoader.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQPluginLoader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQPoint.cc b/src/gsiqt/qt4/QtCore/gsiDeclQPoint.cc index a96ce75bc..2910e0216 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQPoint.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQPoint.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQPointF.cc b/src/gsiqt/qt4/QtCore/gsiDeclQPointF.cc index 830310cdd..19504b591 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQPointF.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQPointF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQProcess.cc b/src/gsiqt/qt4/QtCore/gsiDeclQProcess.cc index e0bb5b421..793e953d5 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQProcess.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQProcess.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQProcessEnvironment.cc b/src/gsiqt/qt4/QtCore/gsiDeclQProcessEnvironment.cc index c1d18c548..8bf10fc02 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQProcessEnvironment.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQProcessEnvironment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQReadLocker.cc b/src/gsiqt/qt4/QtCore/gsiDeclQReadLocker.cc index e1c03673e..ab9873e02 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQReadLocker.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQReadLocker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQReadWriteLock.cc b/src/gsiqt/qt4/QtCore/gsiDeclQReadWriteLock.cc index fe07c43c9..86a10c110 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQReadWriteLock.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQReadWriteLock.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQRect.cc b/src/gsiqt/qt4/QtCore/gsiDeclQRect.cc index 531d18ac0..71bea2873 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQRect.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQRect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQRectF.cc b/src/gsiqt/qt4/QtCore/gsiDeclQRectF.cc index 4b1fba2c9..ee0658878 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQRectF.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQRectF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQRegExp.cc b/src/gsiqt/qt4/QtCore/gsiDeclQRegExp.cc index dce90ff82..c56974b28 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQRegExp.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQRegExp.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQResource.cc b/src/gsiqt/qt4/QtCore/gsiDeclQResource.cc index 93e7f7b4e..0b155ee8d 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQResource.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQResource.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQSemaphore.cc b/src/gsiqt/qt4/QtCore/gsiDeclQSemaphore.cc index 882319bcf..cee3bac17 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQSemaphore.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQSemaphore.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQSettings.cc b/src/gsiqt/qt4/QtCore/gsiDeclQSettings.cc index 0fbd45029..75528551d 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQSettings.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQSettings.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQSignalMapper.cc b/src/gsiqt/qt4/QtCore/gsiDeclQSignalMapper.cc index 304b11606..64f8b9d4d 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQSignalMapper.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQSignalMapper.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQSize.cc b/src/gsiqt/qt4/QtCore/gsiDeclQSize.cc index 656974e9c..3da021af3 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQSize.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQSize.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQSizeF.cc b/src/gsiqt/qt4/QtCore/gsiDeclQSizeF.cc index 1fd75f94f..eff589bf0 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQSizeF.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQSizeF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQSocketNotifier.cc b/src/gsiqt/qt4/QtCore/gsiDeclQSocketNotifier.cc index 1a9a62bcb..8922ce473 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQSocketNotifier.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQSocketNotifier.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQStringMatcher.cc b/src/gsiqt/qt4/QtCore/gsiDeclQStringMatcher.cc index d801a2d54..b3c806ba9 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQStringMatcher.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQStringMatcher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQSysInfo.cc b/src/gsiqt/qt4/QtCore/gsiDeclQSysInfo.cc index f4bad1912..778019825 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQSysInfo.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQSysInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQSystemLocale.cc b/src/gsiqt/qt4/QtCore/gsiDeclQSystemLocale.cc index 27ee29d55..86be1daed 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQSystemLocale.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQSystemLocale.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTemporaryFile.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTemporaryFile.cc index 0b4f8cc57..1ba6fb315 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTemporaryFile.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTemporaryFile.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTextCodec.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTextCodec.cc index ccb8bf157..0dd2cd73b 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTextCodec.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTextCodec.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTextCodec_ConverterState.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTextCodec_ConverterState.cc index b5bfea6fc..c5aadee02 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTextCodec_ConverterState.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTextCodec_ConverterState.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTextDecoder.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTextDecoder.cc index ae7c2eb14..0b14f26f8 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTextDecoder.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTextDecoder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTextEncoder.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTextEncoder.cc index be489a62f..9ad7ccdf9 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTextEncoder.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTextEncoder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTextStream.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTextStream.cc index 403212f89..e0b5c2ef5 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTextStream.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTextStream.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQThread.cc b/src/gsiqt/qt4/QtCore/gsiDeclQThread.cc index ff547f4bc..d31bab25f 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQThread.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQThread.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTime.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTime.cc index 80d7409b0..ca8caf145 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTime.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTime.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTimeLine.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTimeLine.cc index a0412b439..da3bcfdda 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTimeLine.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTimeLine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTimer.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTimer.cc index 9cfd541df..56003e799 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTimer.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTimer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTimerEvent.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTimerEvent.cc index 0f2cb055a..e2947a1a8 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTimerEvent.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTimerEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQTranslator.cc b/src/gsiqt/qt4/QtCore/gsiDeclQTranslator.cc index b7f05208d..2163765e8 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQTranslator.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQTranslator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQUrl.cc b/src/gsiqt/qt4/QtCore/gsiDeclQUrl.cc index 59ec1d7d7..fbba6067f 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQUrl.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQUrl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQWaitCondition.cc b/src/gsiqt/qt4/QtCore/gsiDeclQWaitCondition.cc index 4708ed38d..e9c386b87 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQWaitCondition.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQWaitCondition.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQWriteLocker.cc b/src/gsiqt/qt4/QtCore/gsiDeclQWriteLocker.cc index 561062a86..d151177e7 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQWriteLocker.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQWriteLocker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQt.cc b/src/gsiqt/qt4/QtCore/gsiDeclQt.cc index 4898b3cdf..648017a43 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQt.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQtCoreAdd.cc b/src/gsiqt/qt4/QtCore/gsiDeclQtCoreAdd.cc index e830810bb..3ec2b6f06 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQtCoreAdd.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQtCoreAdd.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQtCoreTypeTraits.h b/src/gsiqt/qt4/QtCore/gsiDeclQtCoreTypeTraits.h index 1231a7be5..633332712 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQtCoreTypeTraits.h +++ b/src/gsiqt/qt4/QtCore/gsiDeclQtCoreTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQt_1.cc b/src/gsiqt/qt4/QtCore/gsiDeclQt_1.cc index 0f3eec9bf..e1025c5c2 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQt_1.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQt_1.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQt_2.cc b/src/gsiqt/qt4/QtCore/gsiDeclQt_2.cc index 2df193044..aec3ee34d 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQt_2.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQt_2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQt_3.cc b/src/gsiqt/qt4/QtCore/gsiDeclQt_3.cc index f4461b24d..0e0fb0686 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQt_3.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQt_3.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtCore/gsiQtExternals.h b/src/gsiqt/qt4/QtCore/gsiQtExternals.h index 83b85324c..924296156 100644 --- a/src/gsiqt/qt4/QtCore/gsiQtExternals.h +++ b/src/gsiqt/qt4/QtCore/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtDesigner/gsiDeclQAbstractFormBuilder.cc b/src/gsiqt/qt4/QtDesigner/gsiDeclQAbstractFormBuilder.cc index 9acd310cb..a67de5a9d 100644 --- a/src/gsiqt/qt4/QtDesigner/gsiDeclQAbstractFormBuilder.cc +++ b/src/gsiqt/qt4/QtDesigner/gsiDeclQAbstractFormBuilder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtDesigner/gsiDeclQFormBuilder.cc b/src/gsiqt/qt4/QtDesigner/gsiDeclQFormBuilder.cc index f5fea2a53..059f3d742 100644 --- a/src/gsiqt/qt4/QtDesigner/gsiDeclQFormBuilder.cc +++ b/src/gsiqt/qt4/QtDesigner/gsiDeclQFormBuilder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtDesigner/gsiDeclQtDesignerTypeTraits.h b/src/gsiqt/qt4/QtDesigner/gsiDeclQtDesignerTypeTraits.h index a17df888f..c0203c33c 100644 --- a/src/gsiqt/qt4/QtDesigner/gsiDeclQtDesignerTypeTraits.h +++ b/src/gsiqt/qt4/QtDesigner/gsiDeclQtDesignerTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtDesigner/gsiQtExternals.h b/src/gsiqt/qt4/QtDesigner/gsiQtExternals.h index 26b07bd2c..8245363c0 100644 --- a/src/gsiqt/qt4/QtDesigner/gsiQtExternals.h +++ b/src/gsiqt/qt4/QtDesigner/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractButton.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractButton.cc index a2102c89a..6dc65331f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractButton.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractGraphicsShapeItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractGraphicsShapeItem.cc index 48a7a3c43..2b51d19fb 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractGraphicsShapeItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractGraphicsShapeItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractItemDelegate.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractItemDelegate.cc index e91c8052a..7f830eccb 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractItemDelegate.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractItemDelegate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractItemView.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractItemView.cc index 6e86b8f91..53380ac28 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractItemView.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractItemView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractPageSetupDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractPageSetupDialog.cc index 61a97fd5c..0ce9482b6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractPageSetupDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractPageSetupDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractPrintDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractPrintDialog.cc index 03ca1b4a0..e4decf277 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractPrintDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractPrintDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractProxyModel.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractProxyModel.cc index c2a546c86..3479664ac 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractProxyModel.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractProxyModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractScrollArea.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractScrollArea.cc index 104e03a3a..48fc8ab93 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractScrollArea.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractScrollArea.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractSlider.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractSlider.cc index e82c3bc41..da148efd0 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractSlider.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractSlider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractSpinBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractSpinBox.cc index 640fd2ac7..8149081ef 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractSpinBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractSpinBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout.cc index 5231fe915..73f7b445c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout_PaintContext.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout_PaintContext.cc index ed7b06755..7f297826d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout_PaintContext.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout_PaintContext.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout_Selection.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout_Selection.cc index 6011bc485..af4e89189 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout_Selection.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractTextDocumentLayout_Selection.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractUndoItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractUndoItem.cc index 50b2ffa55..eb36b0bd1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAbstractUndoItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAbstractUndoItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAccessible.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAccessible.cc index 85cd2c72b..feb310c39 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAccessible.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAccessible.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleApplication.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleApplication.cc index 172f19745..8892d9ab3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleApplication.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleApplication.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleEvent.cc index 48babe0cb..3671b79d8 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleInterface.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleInterface.cc index afba8eb37..673d720a9 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleInterface.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleObject.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleObject.cc index 7f1e919a9..2690bb202 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleObject.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleWidget.cc index bdbc12921..ec5e964de 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAccessibleWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQAction.cc b/src/gsiqt/qt4/QtGui/gsiDeclQAction.cc index 6121a3719..6f1362d2a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQAction.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQAction.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQActionEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQActionEvent.cc index e666dd449..56f31571f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQActionEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQActionEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQActionGroup.cc b/src/gsiqt/qt4/QtGui/gsiDeclQActionGroup.cc index 5dbceafb8..57f448de3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQActionGroup.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQActionGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQApplication.cc b/src/gsiqt/qt4/QtGui/gsiDeclQApplication.cc index 2fe294f5e..f7cfddff2 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQApplication.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQApplication.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQBitmap.cc b/src/gsiqt/qt4/QtGui/gsiDeclQBitmap.cc index e8423237c..59c403bd3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQBitmap.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQBitmap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQBoxLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQBoxLayout.cc index c179b8bb5..086195f8f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQBoxLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQBoxLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQBrush.cc b/src/gsiqt/qt4/QtGui/gsiDeclQBrush.cc index 50e95e4d2..99827f88f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQBrush.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQBrush.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQButtonGroup.cc b/src/gsiqt/qt4/QtGui/gsiDeclQButtonGroup.cc index b20d92ce5..4d32e2579 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQButtonGroup.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQButtonGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQCDEStyle.cc b/src/gsiqt/qt4/QtGui/gsiDeclQCDEStyle.cc index 4d3cfbb27..c5683605c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQCDEStyle.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQCDEStyle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQCalendarWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQCalendarWidget.cc index 51a2e7300..dc87fe23a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQCalendarWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQCalendarWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQCheckBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQCheckBox.cc index 388d3b3eb..d7b819e82 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQCheckBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQCheckBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQCleanlooksStyle.cc b/src/gsiqt/qt4/QtGui/gsiDeclQCleanlooksStyle.cc index 4caea208d..e7a10aed9 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQCleanlooksStyle.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQCleanlooksStyle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQClipboard.cc b/src/gsiqt/qt4/QtGui/gsiDeclQClipboard.cc index 85e31fd33..f4694f63c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQClipboard.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQClipboard.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQClipboardEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQClipboardEvent.cc index c26457ac0..677d4fbc3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQClipboardEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQClipboardEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQCloseEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQCloseEvent.cc index 57410a6aa..11f07366d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQCloseEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQCloseEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQColor.cc b/src/gsiqt/qt4/QtGui/gsiDeclQColor.cc index 8bb48d459..9966155e7 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQColor.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQColor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQColorDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQColorDialog.cc index db2c41851..cff4a280d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQColorDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQColorDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQColormap.cc b/src/gsiqt/qt4/QtGui/gsiDeclQColormap.cc index c33777907..5092a0124 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQColormap.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQColormap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQColumnView.cc b/src/gsiqt/qt4/QtGui/gsiDeclQColumnView.cc index ed60a95b2..5fc706d84 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQColumnView.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQColumnView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQComboBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQComboBox.cc index 2fe4e17bd..81ccc0e2b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQComboBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQComboBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQCommandLinkButton.cc b/src/gsiqt/qt4/QtGui/gsiDeclQCommandLinkButton.cc index f812da124..cc6e531b6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQCommandLinkButton.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQCommandLinkButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQCommonStyle.cc b/src/gsiqt/qt4/QtGui/gsiDeclQCommonStyle.cc index 3760719f9..84dcda42d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQCommonStyle.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQCommonStyle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQCompleter.cc b/src/gsiqt/qt4/QtGui/gsiDeclQCompleter.cc index e755c55c0..272cfc817 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQCompleter.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQCompleter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQConicalGradient.cc b/src/gsiqt/qt4/QtGui/gsiDeclQConicalGradient.cc index c1e320a2d..ab0abbb21 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQConicalGradient.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQConicalGradient.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQContextMenuEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQContextMenuEvent.cc index b1cf5be31..c27e7e863 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQContextMenuEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQContextMenuEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQCursor.cc b/src/gsiqt/qt4/QtGui/gsiDeclQCursor.cc index 225573e15..86de14dd5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQCursor.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQCursor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDataWidgetMapper.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDataWidgetMapper.cc index 46d42f199..cbed56ee4 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDataWidgetMapper.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDataWidgetMapper.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDateEdit.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDateEdit.cc index a9f6de355..4d21231a6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDateEdit.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDateEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDateTimeEdit.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDateTimeEdit.cc index 951d9b2bb..b88d933f6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDateTimeEdit.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDateTimeEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDesktopServices.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDesktopServices.cc index acf7ff3e0..0f013a81a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDesktopServices.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDesktopServices.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDesktopWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDesktopWidget.cc index e2d86f057..b00cd090c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDesktopWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDesktopWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDial.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDial.cc index 3aaeb3fd3..bbae271ac 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDial.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDial.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDialog.cc index 68ec91f3c..ac872b642 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDialogButtonBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDialogButtonBox.cc index 9b9faceb2..70c3a1003 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDialogButtonBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDialogButtonBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDirIterator.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDirIterator.cc index b356dff45..3fcd35ee3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDirIterator.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDirIterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDirModel.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDirModel.cc index 763c78a29..5689be089 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDirModel.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDirModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDockWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDockWidget.cc index 6b1a0d83b..5c37909d0 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDockWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDockWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDoubleSpinBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDoubleSpinBox.cc index b2ed8dde5..f3b9fdebe 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDoubleSpinBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDoubleSpinBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDoubleValidator.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDoubleValidator.cc index abd61f896..546fede71 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDoubleValidator.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDoubleValidator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDrag.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDrag.cc index f34bc5cf2..c873dc03a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDrag.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDrag.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDragEnterEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDragEnterEvent.cc index 3636a9b34..6b4304900 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDragEnterEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDragEnterEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDragLeaveEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDragLeaveEvent.cc index b87c6aec0..6e8843aa6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDragLeaveEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDragLeaveEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDragMoveEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDragMoveEvent.cc index 66b82402e..69625aaf1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDragMoveEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDragMoveEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDragResponseEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDragResponseEvent.cc index 4d724c740..319c7bc81 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDragResponseEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDragResponseEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDropEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDropEvent.cc index 069b1d33b..5f5a71db4 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDropEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDropEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQErrorMessage.cc b/src/gsiqt/qt4/QtGui/gsiDeclQErrorMessage.cc index 6be0a930d..85ea32098 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQErrorMessage.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQErrorMessage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFileDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFileDialog.cc index 34d2f389d..5aacb0703 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFileDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFileDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFileIconProvider.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFileIconProvider.cc index c4e2561c4..da4389198 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFileIconProvider.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFileIconProvider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFileOpenEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFileOpenEvent.cc index 461ee5b44..ce5c13e23 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFileOpenEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFileOpenEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFileSystemModel.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFileSystemModel.cc index b34506017..d5b09dce5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFileSystemModel.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFileSystemModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFocusEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFocusEvent.cc index 6c533c0c5..70ebaab3d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFocusEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFocusEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFocusFrame.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFocusFrame.cc index 71a262369..039cb6afe 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFocusFrame.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFocusFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFont.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFont.cc index 0c4a1293e..76fa44557 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFont.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFont.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFontComboBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFontComboBox.cc index 0c3dda7de..1410fb676 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFontComboBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFontComboBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFontDatabase.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFontDatabase.cc index 6264e347c..b22655625 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFontDatabase.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFontDatabase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFontDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFontDialog.cc index 7ea03db36..8fb39cc5c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFontDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFontDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFontInfo.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFontInfo.cc index 6d4144a18..dd002a7f7 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFontInfo.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFontInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFontMetrics.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFontMetrics.cc index 86ac933f0..4bef71f67 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFontMetrics.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFontMetrics.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFontMetricsF.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFontMetricsF.cc index 4c1d09cb1..c7b8ba9a9 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFontMetricsF.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFontMetricsF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFormLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFormLayout.cc index 97397304c..e50e07089 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFormLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFormLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQFrame.cc b/src/gsiqt/qt4/QtGui/gsiDeclQFrame.cc index 1a5b691a2..f090134e2 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQFrame.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGesture.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGesture.cc index 164de202b..b60930d26 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGesture.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGestureEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGestureEvent.cc index 5aa446c6c..e2b48b43a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGestureEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGestureEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGestureRecognizer.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGestureRecognizer.cc index fab030f25..3cf17ee5a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGestureRecognizer.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGestureRecognizer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGradient.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGradient.cc index 287f86443..29db05318 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGradient.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGradient.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsAnchor.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsAnchor.cc index 8368a5ab9..55a92147a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsAnchor.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsAnchor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsAnchorLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsAnchorLayout.cc index e944bf019..52c2a0fe5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsAnchorLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsAnchorLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsBlurEffect.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsBlurEffect.cc index 1c04a0772..88d64a425 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsBlurEffect.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsBlurEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsColorizeEffect.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsColorizeEffect.cc index 0c35e854c..05b35257d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsColorizeEffect.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsColorizeEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsDropShadowEffect.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsDropShadowEffect.cc index 6ec9152e6..66e4def9d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsDropShadowEffect.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsDropShadowEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsEffect.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsEffect.cc index 87e7f3a24..758b4a4e6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsEffect.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsEllipseItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsEllipseItem.cc index 5f05106d9..4e5aa1b2b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsEllipseItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsEllipseItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsGridLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsGridLayout.cc index 7c84614ac..3c2415bf9 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsGridLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsGridLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItem.cc index 2fda27680..2a065bf57 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItemAnimation.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItemAnimation.cc index 777ed985a..c77e64548 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItemAnimation.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItemAnimation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItemGroup.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItemGroup.cc index 9fd8557a5..a22e5d545 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItemGroup.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsItemGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLayout.cc index 86a3dfdd5..097362513 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLayoutItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLayoutItem.cc index e995b7aa2..fbd01736f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLayoutItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLayoutItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLineItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLineItem.cc index b12b7cfef..0267c1488 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLineItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLineItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLinearLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLinearLayout.cc index 45da781f6..415242b9b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLinearLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsLinearLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsObject.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsObject.cc index bfdd04cfc..ca1a8b5cf 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsObject.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsOpacityEffect.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsOpacityEffect.cc index 8c9f92664..f4ebbc7c6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsOpacityEffect.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsOpacityEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPathItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPathItem.cc index 4b3dc2623..5a0377721 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPathItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPathItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPixmapItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPixmapItem.cc index 34e38bb5f..49cc959cd 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPixmapItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPixmapItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPolygonItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPolygonItem.cc index 346a7bb89..b53959c42 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPolygonItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsPolygonItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsProxyWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsProxyWidget.cc index 539a68679..a752235e6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsProxyWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsProxyWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsRectItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsRectItem.cc index 76d85ba60..19560212f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsRectItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsRectItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsRotation.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsRotation.cc index 42f8bb247..546205867 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsRotation.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsRotation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsScale.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsScale.cc index 1145a522f..dec630c02 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsScale.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsScale.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsScene.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsScene.cc index 1e1d44c1e..5ce8b6d59 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsScene.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsScene.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneContextMenuEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneContextMenuEvent.cc index 0831be2d7..35305ab01 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneContextMenuEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneContextMenuEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneDragDropEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneDragDropEvent.cc index 01b8c7f37..6a788a9e1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneDragDropEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneDragDropEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneEvent.cc index 07ea1c8a0..6be113df7 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneHelpEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneHelpEvent.cc index a866bbcf9..571bd9c87 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneHelpEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneHelpEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneHoverEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneHoverEvent.cc index 98405f63e..848fa143d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneHoverEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneHoverEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneMouseEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneMouseEvent.cc index 3121ae1e6..fe1c871e8 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneMouseEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneMouseEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneMoveEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneMoveEvent.cc index e649ef59b..7ef888b41 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneMoveEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneMoveEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneResizeEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneResizeEvent.cc index b5569d9bb..68fa93fd1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneResizeEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneResizeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneWheelEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneWheelEvent.cc index cddd12c7c..3d14343e5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneWheelEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSceneWheelEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSimpleTextItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSimpleTextItem.cc index fe72a7074..3db516ce2 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSimpleTextItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsSimpleTextItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsTextItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsTextItem.cc index 942fc8d6c..15a293955 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsTextItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsTextItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsTransform.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsTransform.cc index a2bd29eb3..2e20db606 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsTransform.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsTransform.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsView.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsView.cc index 6cdb297e9..cc8cd4eff 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsView.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsWidget.cc index 1a1e112df..dc3aac3a7 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGridLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGridLayout.cc index dced0b232..050647640 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGridLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGridLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGroupBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGroupBox.cc index 914b72248..a197823b7 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGroupBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGroupBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQHBoxLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQHBoxLayout.cc index 45334060c..0ced6feb1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQHBoxLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQHBoxLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQHeaderView.cc b/src/gsiqt/qt4/QtGui/gsiDeclQHeaderView.cc index 056ae09f7..c76f2fc5d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQHeaderView.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQHeaderView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQHelpEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQHelpEvent.cc index 77772171a..a4075459f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQHelpEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQHelpEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQHideEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQHideEvent.cc index 162f2701e..2b29624e6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQHideEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQHideEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQHoverEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQHoverEvent.cc index 9669b591c..5f12c8a5a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQHoverEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQHoverEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQIcon.cc b/src/gsiqt/qt4/QtGui/gsiDeclQIcon.cc index 617d4923d..85fd22aa7 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQIcon.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQIcon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQIconDragEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQIconDragEvent.cc index 79ae4acb0..ac443e9ba 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQIconDragEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQIconDragEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQIconEngine.cc b/src/gsiqt/qt4/QtGui/gsiDeclQIconEngine.cc index e2411d5a6..55a8fa157 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQIconEngine.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQIconEngine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQIconEnginePlugin.cc b/src/gsiqt/qt4/QtGui/gsiDeclQIconEnginePlugin.cc index b9651d57b..c73ba51b0 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQIconEnginePlugin.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQIconEnginePlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQIconEnginePluginV2.cc b/src/gsiqt/qt4/QtGui/gsiDeclQIconEnginePluginV2.cc index 2cdd0facd..5e41bba16 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQIconEnginePluginV2.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQIconEnginePluginV2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQIconEngineV2.cc b/src/gsiqt/qt4/QtGui/gsiDeclQIconEngineV2.cc index 65ce454ed..6d57c5123 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQIconEngineV2.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQIconEngineV2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQImage.cc b/src/gsiqt/qt4/QtGui/gsiDeclQImage.cc index 586fdc6ac..757f256b5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQImage.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQImage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQImageIOHandler.cc b/src/gsiqt/qt4/QtGui/gsiDeclQImageIOHandler.cc index 946b486c0..1d69a6a2e 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQImageIOHandler.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQImageIOHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQImageIOPlugin.cc b/src/gsiqt/qt4/QtGui/gsiDeclQImageIOPlugin.cc index c27ffc417..950a568ca 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQImageIOPlugin.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQImageIOPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQImageReader.cc b/src/gsiqt/qt4/QtGui/gsiDeclQImageReader.cc index 7d4c29b5c..1a6bffeee 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQImageReader.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQImageReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQImageTextKeyLang.cc b/src/gsiqt/qt4/QtGui/gsiDeclQImageTextKeyLang.cc index 7f37269a8..e6f69df08 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQImageTextKeyLang.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQImageTextKeyLang.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQImageWriter.cc b/src/gsiqt/qt4/QtGui/gsiDeclQImageWriter.cc index f7b4af7b7..8eb1f4346 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQImageWriter.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQImageWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQInputContext.cc b/src/gsiqt/qt4/QtGui/gsiDeclQInputContext.cc index 0ec1ea542..4e07db860 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQInputContext.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQInputContext.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQInputContextFactory.cc b/src/gsiqt/qt4/QtGui/gsiDeclQInputContextFactory.cc index 16ea5ce6e..6f966e6c8 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQInputContextFactory.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQInputContextFactory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQInputContextPlugin.cc b/src/gsiqt/qt4/QtGui/gsiDeclQInputContextPlugin.cc index 6420c77fa..2cc96132d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQInputContextPlugin.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQInputContextPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQInputDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQInputDialog.cc index d3e6037ac..fe9ab3454 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQInputDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQInputDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQInputEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQInputEvent.cc index f89cddec4..16a2e90ee 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQInputEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQInputEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQInputMethodEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQInputMethodEvent.cc index c40dfce61..35f94eb39 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQInputMethodEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQInputMethodEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQInputMethodEvent_Attribute.cc b/src/gsiqt/qt4/QtGui/gsiDeclQInputMethodEvent_Attribute.cc index 406e68321..32609e53d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQInputMethodEvent_Attribute.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQInputMethodEvent_Attribute.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQIntValidator.cc b/src/gsiqt/qt4/QtGui/gsiDeclQIntValidator.cc index 3e5fc22d5..cfb9ee8ec 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQIntValidator.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQIntValidator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQItemDelegate.cc b/src/gsiqt/qt4/QtGui/gsiDeclQItemDelegate.cc index c010bbee7..95d3cd45e 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQItemDelegate.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQItemDelegate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQItemEditorCreatorBase.cc b/src/gsiqt/qt4/QtGui/gsiDeclQItemEditorCreatorBase.cc index 3c709f35c..ecf646471 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQItemEditorCreatorBase.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQItemEditorCreatorBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQItemEditorFactory.cc b/src/gsiqt/qt4/QtGui/gsiDeclQItemEditorFactory.cc index ae5d1de53..c750cf536 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQItemEditorFactory.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQItemEditorFactory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQItemSelection.cc b/src/gsiqt/qt4/QtGui/gsiDeclQItemSelection.cc index d6caacc4c..6b9818b32 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQItemSelection.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQItemSelection.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQItemSelectionModel.cc b/src/gsiqt/qt4/QtGui/gsiDeclQItemSelectionModel.cc index c37e28a3c..f4b75c5d7 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQItemSelectionModel.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQItemSelectionModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQItemSelectionRange.cc b/src/gsiqt/qt4/QtGui/gsiDeclQItemSelectionRange.cc index a637954bd..4e30f5327 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQItemSelectionRange.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQItemSelectionRange.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQKeyEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQKeyEvent.cc index 2c74c045d..a64c1c94c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQKeyEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQKeyEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQKeySequence.cc b/src/gsiqt/qt4/QtGui/gsiDeclQKeySequence.cc index a1e655873..4e3c03f99 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQKeySequence.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQKeySequence.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQLCDNumber.cc b/src/gsiqt/qt4/QtGui/gsiDeclQLCDNumber.cc index d78e69e92..1964539c3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQLCDNumber.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQLCDNumber.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQLabel.cc b/src/gsiqt/qt4/QtGui/gsiDeclQLabel.cc index 4f6155b65..7b2a2ac89 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQLabel.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQLabel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQLayout.cc index 068ca4795..b0314be34 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQLayoutItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQLayoutItem.cc index c4adee29e..e36ac2bb6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQLayoutItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQLayoutItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQLineEdit.cc b/src/gsiqt/qt4/QtGui/gsiDeclQLineEdit.cc index fd5b4d82e..47979e0a3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQLineEdit.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQLineEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQLinearGradient.cc b/src/gsiqt/qt4/QtGui/gsiDeclQLinearGradient.cc index f23620fe4..d4ea4aaac 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQLinearGradient.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQLinearGradient.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQListView.cc b/src/gsiqt/qt4/QtGui/gsiDeclQListView.cc index dac7d70b3..31c828c41 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQListView.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQListView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQListWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQListWidget.cc index 4a5811735..3b2f26a29 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQListWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQListWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQListWidgetItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQListWidgetItem.cc index 3333d0eb4..e0aa5ba28 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQListWidgetItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQListWidgetItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMainWindow.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMainWindow.cc index f1a3af77d..9e6cd6706 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMainWindow.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMainWindow.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMatrix.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMatrix.cc index 066ae2c89..6c14a83c9 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMatrix.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMatrix.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMatrix4x4.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMatrix4x4.cc index 107d4b2fd..2a4d05ef1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMatrix4x4.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMatrix4x4.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMdiArea.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMdiArea.cc index c1eb308fc..e2a3d7bc3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMdiArea.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMdiArea.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMdiSubWindow.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMdiSubWindow.cc index c28d0fcb4..d8e077c42 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMdiSubWindow.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMdiSubWindow.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMenu.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMenu.cc index b32478e55..aa19eaf7c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMenu.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMenu.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMenuBar.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMenuBar.cc index 5e5520c44..6a7093afe 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMenuBar.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMenuBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMessageBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMessageBox.cc index 80b8f83f5..99c8b47fa 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMessageBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMessageBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMimeSource.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMimeSource.cc index 3c376fe5f..a2a20d01e 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMimeSource.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMimeSource.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMotifStyle.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMotifStyle.cc index b9aeda049..4ca163c5f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMotifStyle.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMotifStyle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMouseEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMouseEvent.cc index 2a3ae9fad..681a08b27 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMouseEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMouseEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMoveEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMoveEvent.cc index 18a964743..a366660c8 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMoveEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMoveEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQMovie.cc b/src/gsiqt/qt4/QtGui/gsiDeclQMovie.cc index 0f48725c8..7441d4514 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQMovie.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQMovie.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPageSetupDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPageSetupDialog.cc index d70e843bc..79f89bf39 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPageSetupDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPageSetupDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPaintDevice.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPaintDevice.cc index 211e21c02..5eda3080b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPaintDevice.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPaintDevice.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPaintEngine.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPaintEngine.cc index 9255cef48..8018fae4a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPaintEngine.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPaintEngine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPaintEngineState.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPaintEngineState.cc index 24aa70e34..d72435c0b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPaintEngineState.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPaintEngineState.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPaintEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPaintEvent.cc index a01a9e8e8..6239b1d0d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPaintEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPaintEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPainter.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPainter.cc index 6f68f77ad..068277e6a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPainter.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPainter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPainterPath.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPainterPath.cc index 2c6812e9e..0b062ffa3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPainterPath.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPainterPath.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPainterPathStroker.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPainterPathStroker.cc index 7efde0bc8..89c9c7405 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPainterPathStroker.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPainterPathStroker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPainterPath_Element.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPainterPath_Element.cc index dfe8b9935..c5770b0ba 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPainterPath_Element.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPainterPath_Element.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPalette.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPalette.cc index 3df5de095..74af8c519 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPalette.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPalette.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPanGesture.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPanGesture.cc index 4883a2104..2a6249ab2 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPanGesture.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPanGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPen.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPen.cc index 4da3299ef..7863641e1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPen.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPen.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPicture.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPicture.cc index d0e1bc61f..797373b23 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPicture.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPicture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPinchGesture.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPinchGesture.cc index 01e914278..89c73ec20 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPinchGesture.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPinchGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPixmap.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPixmap.cc index 1adb7f5ee..ee3c078b7 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPixmap.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPixmap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPixmapCache.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPixmapCache.cc index 85d677fad..625c45db3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPixmapCache.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPixmapCache.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPlainTextDocumentLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPlainTextDocumentLayout.cc index ac0822a8a..161aa3121 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPlainTextDocumentLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPlainTextDocumentLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPlainTextEdit.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPlainTextEdit.cc index b962eb746..6a7f696b1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPlainTextEdit.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPlainTextEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPlastiqueStyle.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPlastiqueStyle.cc index c28b22cfa..299431fa4 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPlastiqueStyle.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPlastiqueStyle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPolygon.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPolygon.cc index a9424cfd1..a8655320d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPolygon.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPolygon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPolygonF.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPolygonF.cc index 49b85db66..70b86e8de 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPolygonF.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPolygonF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPrintDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPrintDialog.cc index ae78bbcb2..fe5d9a7a6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPrintDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPrintDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPrintEngine.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPrintEngine.cc index 68a07f539..119eb323c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPrintEngine.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPrintEngine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPrintPreviewDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPrintPreviewDialog.cc index f3112e81d..060ee3be8 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPrintPreviewDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPrintPreviewDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPrintPreviewWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPrintPreviewWidget.cc index bbabe8d56..4ee5852d3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPrintPreviewWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPrintPreviewWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPrinter.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPrinter.cc index e225c0156..fdc775f6b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPrinter.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPrinter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPrinterInfo.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPrinterInfo.cc index dd5ddae9a..610c0f8a2 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPrinterInfo.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPrinterInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQProgressBar.cc b/src/gsiqt/qt4/QtGui/gsiDeclQProgressBar.cc index d8d42d89b..341f9d39a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQProgressBar.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQProgressBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQProgressDialog.cc b/src/gsiqt/qt4/QtGui/gsiDeclQProgressDialog.cc index 4f23103cd..49a613436 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQProgressDialog.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQProgressDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPushButton.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPushButton.cc index 3c29e70e7..163afc410 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPushButton.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPushButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQQuaternion.cc b/src/gsiqt/qt4/QtGui/gsiDeclQQuaternion.cc index c928961dd..6631bee2d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQQuaternion.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQQuaternion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQRadialGradient.cc b/src/gsiqt/qt4/QtGui/gsiDeclQRadialGradient.cc index 495667506..7a1db05a1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQRadialGradient.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQRadialGradient.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQRadioButton.cc b/src/gsiqt/qt4/QtGui/gsiDeclQRadioButton.cc index 84d2b5f58..528ae9ad8 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQRadioButton.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQRadioButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQRegExpValidator.cc b/src/gsiqt/qt4/QtGui/gsiDeclQRegExpValidator.cc index 828fa888e..07020161f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQRegExpValidator.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQRegExpValidator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQRegion.cc b/src/gsiqt/qt4/QtGui/gsiDeclQRegion.cc index 877e8ad6e..2181de836 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQRegion.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQRegion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQResizeEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQResizeEvent.cc index d4a8f2bf4..85723f8e5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQResizeEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQResizeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQRubberBand.cc b/src/gsiqt/qt4/QtGui/gsiDeclQRubberBand.cc index 9cfcecb4c..54108e255 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQRubberBand.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQRubberBand.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQScrollArea.cc b/src/gsiqt/qt4/QtGui/gsiDeclQScrollArea.cc index 980dd59e9..39cf28e37 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQScrollArea.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQScrollArea.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQScrollBar.cc b/src/gsiqt/qt4/QtGui/gsiDeclQScrollBar.cc index 117a5f2bb..1a2be80e4 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQScrollBar.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQScrollBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQShortcut.cc b/src/gsiqt/qt4/QtGui/gsiDeclQShortcut.cc index 3aa0a4abb..9fceb9a0c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQShortcut.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQShortcut.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQShortcutEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQShortcutEvent.cc index da3551f47..2d0b52773 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQShortcutEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQShortcutEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQShowEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQShowEvent.cc index 34f2bfb71..e6e93bb91 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQShowEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQShowEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSizeGrip.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSizeGrip.cc index 6c8f6df52..41f799e31 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSizeGrip.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSizeGrip.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSizePolicy.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSizePolicy.cc index 58b2ce762..5e4e26735 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSizePolicy.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSizePolicy.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSlider.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSlider.cc index c80376159..f2596b80b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSlider.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSlider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSortFilterProxyModel.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSortFilterProxyModel.cc index 56283f93a..7630e097c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSortFilterProxyModel.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSortFilterProxyModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSound.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSound.cc index eb65765ed..19c7f108c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSound.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSound.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSpacerItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSpacerItem.cc index e01d5bc8e..e1cd31937 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSpacerItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSpacerItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSpinBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSpinBox.cc index 9f877e121..00efc33d3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSpinBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSpinBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSplashScreen.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSplashScreen.cc index 3fb3804c1..d8013d031 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSplashScreen.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSplashScreen.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSplitter.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSplitter.cc index 9e12fdd4b..5d6053a3b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSplitter.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSplitter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSplitterHandle.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSplitterHandle.cc index 261a6c8d8..57d10eee1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSplitterHandle.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSplitterHandle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStackedLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStackedLayout.cc index b98e53e77..5c0222e24 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStackedLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStackedLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStackedWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStackedWidget.cc index 69b261f21..0383849b5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStackedWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStackedWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStandardItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStandardItem.cc index f42ffc5c0..a1e2ed666 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStandardItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStandardItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStandardItemModel.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStandardItemModel.cc index 58309c7ca..6abcde73d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStandardItemModel.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStandardItemModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStatusBar.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStatusBar.cc index c7d020fa2..8b9faa014 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStatusBar.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStatusBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStatusTipEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStatusTipEvent.cc index 9f63b58a4..19282eead 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStatusTipEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStatusTipEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStringListModel.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStringListModel.cc index 28a3092ff..8ccd11f9a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStringListModel.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStringListModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyle.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyle.cc index a80491ffc..9a29827d5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyle.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleFactory.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleFactory.cc index c4eb68b2c..d4cdff47a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleFactory.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleFactory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturn.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturn.cc index e79a9ecb9..dc838587c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturn.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturn.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturnMask.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturnMask.cc index 495fc6276..19ff40825 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturnMask.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturnMask.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturnVariant.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturnVariant.cc index 99e70162f..ad2a888da 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturnVariant.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleHintReturnVariant.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOption.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOption.cc index 663ba9c11..ecb92e218 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOption.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOption.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionButton.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionButton.cc index 45567893a..23f923e90 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionButton.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionComboBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionComboBox.cc index 4bcd4b40d..070569f02 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionComboBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionComboBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionComplex.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionComplex.cc index cd236b3ac..868e44e4f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionComplex.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionComplex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionDockWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionDockWidget.cc index ca3c43639..ff3edb44b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionDockWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionDockWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFocusRect.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFocusRect.cc index d65d343f3..7b5b24e85 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFocusRect.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFocusRect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrame.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrame.cc index b3ad37c77..6103571c3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrame.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrameV2.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrameV2.cc index 61f8e9923..9e72fd455 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrameV2.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrameV2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrameV3.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrameV3.cc index d065207ad..70fd1b8d9 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrameV3.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionFrameV3.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionGraphicsItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionGraphicsItem.cc index 5eb670315..8f7482f1e 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionGraphicsItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionGraphicsItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionGroupBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionGroupBox.cc index 2b830a18d..4741efe70 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionGroupBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionGroupBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionHeader.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionHeader.cc index 5de2a41ff..84d241b10 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionHeader.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionHeader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionMenuItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionMenuItem.cc index 4b7a0edda..c73c608f8 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionMenuItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionMenuItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionProgressBar.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionProgressBar.cc index 2d8cb5a43..111f42236 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionProgressBar.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionProgressBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionProgressBarV2.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionProgressBarV2.cc index 0249e55e7..77c9f56c3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionProgressBarV2.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionProgressBarV2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3DockWindow.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3DockWindow.cc index 03ccec881..a9e057787 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3DockWindow.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3DockWindow.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3ListView.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3ListView.cc index 1da4ce607..8a26cc165 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3ListView.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3ListView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3ListViewItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3ListViewItem.cc index adb896f89..af1074db2 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3ListViewItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionQ3ListViewItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionRubberBand.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionRubberBand.cc index f0aa182f2..147456f97 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionRubberBand.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionRubberBand.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSizeGrip.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSizeGrip.cc index 110de1f1d..0004ab703 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSizeGrip.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSizeGrip.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSlider.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSlider.cc index e1271c2e5..dcc13ff58 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSlider.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSlider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSpinBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSpinBox.cc index 5c2966637..51254eea3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSpinBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionSpinBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTab.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTab.cc index 6ed982fcc..f667f4913 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTab.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTab.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabBarBase.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabBarBase.cc index 38440cd86..f8900490f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabBarBase.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabBarBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabBarBaseV2.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabBarBaseV2.cc index 407099212..2dde55e36 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabBarBaseV2.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabBarBaseV2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabV2.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabV2.cc index 141178229..aed3410c4 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabV2.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabV2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabV3.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabV3.cc index 05ec47bc2..fcf25c7a8 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabV3.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabV3.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabWidgetFrame.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabWidgetFrame.cc index ca6ea3125..e4d6809ab 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabWidgetFrame.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTabWidgetFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTitleBar.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTitleBar.cc index c1ec9cb87..beb973f8c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTitleBar.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionTitleBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBar.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBar.cc index 27e8e8129..0b17d826d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBar.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBox.cc index 9e876ed1f..85748fdaf 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBoxV2.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBoxV2.cc index 0ea7881d8..88b45f4d4 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBoxV2.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolBoxV2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolButton.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolButton.cc index 091954456..035dac553 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolButton.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionToolButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItem.cc index c9791d6c9..03d2da5be 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV2.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV2.cc index eec7ccc0b..4745a62d4 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV2.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV3.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV3.cc index 4980f76cb..8fa4826c4 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV3.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV3.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV4.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV4.cc index 6e9ada11a..c7aa0ed22 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV4.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyleOptionViewItemV4.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStylePainter.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStylePainter.cc index a4e2b8df8..5f0101083 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStylePainter.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStylePainter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStylePlugin.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStylePlugin.cc index 79fe35fa6..ec8989c17 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStylePlugin.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStylePlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQStyledItemDelegate.cc b/src/gsiqt/qt4/QtGui/gsiDeclQStyledItemDelegate.cc index 9e2047ca7..3dc51793b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQStyledItemDelegate.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQStyledItemDelegate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSwipeGesture.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSwipeGesture.cc index d835ec3b2..5f1154878 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSwipeGesture.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSwipeGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSyntaxHighlighter.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSyntaxHighlighter.cc index 36d1b0ffa..48735becf 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSyntaxHighlighter.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSyntaxHighlighter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQSystemTrayIcon.cc b/src/gsiqt/qt4/QtGui/gsiDeclQSystemTrayIcon.cc index 06956c685..09b7258c3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQSystemTrayIcon.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQSystemTrayIcon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTabBar.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTabBar.cc index 1f34a6e93..5442a8e9f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTabBar.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTabBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTabWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTabWidget.cc index 2e00d97fe..3435c9c17 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTabWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTabWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTableView.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTableView.cc index ebbea8177..84432db93 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTableView.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTableView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTableWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTableWidget.cc index b6e4bd294..92f18b21f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTableWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTableWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTableWidgetItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTableWidgetItem.cc index db29efb8d..40ad4ce98 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTableWidgetItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTableWidgetItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTableWidgetSelectionRange.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTableWidgetSelectionRange.cc index f3341bf11..38c8c5be0 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTableWidgetSelectionRange.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTableWidgetSelectionRange.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTabletEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTabletEvent.cc index 8fbfe54d5..566696e9b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTabletEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTabletEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTapAndHoldGesture.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTapAndHoldGesture.cc index 03abce365..1c1956569 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTapAndHoldGesture.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTapAndHoldGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTapGesture.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTapGesture.cc index d44c42d86..8752cd45b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTapGesture.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTapGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextBlock.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextBlock.cc index 1023c0f33..4468bc5ec 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextBlock.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextBlock.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockFormat.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockFormat.cc index e86133854..d772d3b11 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockFormat.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockGroup.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockGroup.cc index 3568ca34c..e81312f2b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockGroup.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockUserData.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockUserData.cc index 7e869019e..ef4259ae5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockUserData.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextBlockUserData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextBlock_Iterator.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextBlock_Iterator.cc index 6659c42a2..84f1eceef 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextBlock_Iterator.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextBlock_Iterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextBrowser.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextBrowser.cc index 443a2ab55..43b6d88f9 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextBrowser.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextBrowser.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextCharFormat.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextCharFormat.cc index 8f1ee480d..ec24ec5b5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextCharFormat.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextCharFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextCursor.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextCursor.cc index 7e2d3c9e9..745c76b93 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextCursor.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextCursor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextDocument.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextDocument.cc index 2fe29557e..182e91c0a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextDocument.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextDocument.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextDocumentFragment.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextDocumentFragment.cc index 233221f25..f54798e65 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextDocumentFragment.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextDocumentFragment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextDocumentWriter.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextDocumentWriter.cc index dda660fc8..5dff3682f 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextDocumentWriter.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextDocumentWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextEdit.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextEdit.cc index 36b7799c4..fddda93eb 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextEdit.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextEdit_ExtraSelection.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextEdit_ExtraSelection.cc index f9b24488a..fe8e8b3cf 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextEdit_ExtraSelection.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextEdit_ExtraSelection.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextFormat.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextFormat.cc index 5be52459a..eb431af0a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextFormat.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextFragment.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextFragment.cc index 12f5da403..541dbf138 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextFragment.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextFragment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextFrame.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextFrame.cc index 5e3d35760..771045336 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextFrame.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextFrameFormat.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextFrameFormat.cc index 778aeeb63..213f46c18 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextFrameFormat.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextFrameFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextFrame_Iterator.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextFrame_Iterator.cc index ece110cbe..c7420b980 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextFrame_Iterator.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextFrame_Iterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextImageFormat.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextImageFormat.cc index 44e42b87d..af11e1bef 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextImageFormat.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextImageFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextInlineObject.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextInlineObject.cc index 1bdf78daa..dfb07a91a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextInlineObject.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextInlineObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextItem.cc index 3c1bb41ff..14521e6ee 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextLayout.cc index cdbe5a8d7..e45070ffe 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextLayout_FormatRange.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextLayout_FormatRange.cc index 4cbe7c962..e9d84462e 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextLayout_FormatRange.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextLayout_FormatRange.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextLength.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextLength.cc index 9b97d91e3..7bc0e4b80 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextLength.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextLength.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextLine.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextLine.cc index 88afd7d6d..5b4d48b0a 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextLine.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextLine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextList.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextList.cc index a93ba2f28..0be50383b 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextList.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextList.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextListFormat.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextListFormat.cc index d429e4aa3..6350f4de9 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextListFormat.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextListFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextObject.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextObject.cc index f96edaeb7..b92c84972 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextObject.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextObjectInterface.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextObjectInterface.cc index 6060a3eb9..6247525c2 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextObjectInterface.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextObjectInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextOption.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextOption.cc index e7919f5db..afbac01fa 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextOption.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextOption.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextOption_Tab.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextOption_Tab.cc index 2b9a5c8bb..336bf7915 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextOption_Tab.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextOption_Tab.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextTable.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextTable.cc index da13dd152..76412f6ba 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextTable.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextTable.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextTableCell.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextTableCell.cc index 2403c7628..28b679e8d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextTableCell.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextTableCell.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextTableCellFormat.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextTableCellFormat.cc index 54e54493a..f8074f0d3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextTableCellFormat.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextTableCellFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTextTableFormat.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTextTableFormat.cc index 88aa993be..121026041 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTextTableFormat.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTextTableFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTimeEdit.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTimeEdit.cc index f5117d60e..e2ede9f98 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTimeEdit.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTimeEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQToolBar.cc b/src/gsiqt/qt4/QtGui/gsiDeclQToolBar.cc index 95f6b3650..8c63cae86 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQToolBar.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQToolBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQToolBarChangeEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQToolBarChangeEvent.cc index 442138323..554b91457 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQToolBarChangeEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQToolBarChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQToolBox.cc b/src/gsiqt/qt4/QtGui/gsiDeclQToolBox.cc index 56614811c..45c964e11 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQToolBox.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQToolBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQToolButton.cc b/src/gsiqt/qt4/QtGui/gsiDeclQToolButton.cc index 0b7148f6c..46c846e90 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQToolButton.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQToolButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQToolTip.cc b/src/gsiqt/qt4/QtGui/gsiDeclQToolTip.cc index 04306288d..2576547d2 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQToolTip.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQToolTip.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTouchEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTouchEvent.cc index 8c926a141..b1c150e38 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTouchEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTouchEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTouchEvent_TouchPoint.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTouchEvent_TouchPoint.cc index df17f929e..64107d230 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTouchEvent_TouchPoint.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTouchEvent_TouchPoint.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTransform.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTransform.cc index 5719f860e..6ac40ed49 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTransform.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTransform.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTreeView.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTreeView.cc index 77fcb5cb0..3e02aa28d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTreeView.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTreeView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidget.cc index 4ba0a8741..8002b1f35 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidgetItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidgetItem.cc index 69ce7db65..d24ca76e5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidgetItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidgetItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidgetItemIterator.cc b/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidgetItemIterator.cc index 387994d07..545609377 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidgetItemIterator.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQTreeWidgetItemIterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQUndoCommand.cc b/src/gsiqt/qt4/QtGui/gsiDeclQUndoCommand.cc index 7865f6e71..88e66bcd9 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQUndoCommand.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQUndoCommand.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQUndoGroup.cc b/src/gsiqt/qt4/QtGui/gsiDeclQUndoGroup.cc index 1dc65f840..576d59058 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQUndoGroup.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQUndoGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQUndoStack.cc b/src/gsiqt/qt4/QtGui/gsiDeclQUndoStack.cc index bd2224cbe..84445f553 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQUndoStack.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQUndoStack.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQUndoView.cc b/src/gsiqt/qt4/QtGui/gsiDeclQUndoView.cc index c94e8ee45..2c03582b2 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQUndoView.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQUndoView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQUnixPrintWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQUnixPrintWidget.cc index 80ab9931d..0e584d9f5 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQUnixPrintWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQUnixPrintWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQVBoxLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQVBoxLayout.cc index 446c21fde..c28e54017 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQVBoxLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQVBoxLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQValidator.cc b/src/gsiqt/qt4/QtGui/gsiDeclQValidator.cc index 91c728a7d..f718cb487 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQValidator.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQValidator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQVector2D.cc b/src/gsiqt/qt4/QtGui/gsiDeclQVector2D.cc index 229ff6a07..5ebae12dd 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQVector2D.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQVector2D.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQVector3D.cc b/src/gsiqt/qt4/QtGui/gsiDeclQVector3D.cc index 3a4937c3d..d2268c974 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQVector3D.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQVector3D.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQVector4D.cc b/src/gsiqt/qt4/QtGui/gsiDeclQVector4D.cc index 6ae8215db..4c8f38838 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQVector4D.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQVector4D.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWhatsThis.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWhatsThis.cc index 235ca31c9..d8fead706 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWhatsThis.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWhatsThis.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWhatsThisClickedEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWhatsThisClickedEvent.cc index 37905e62c..150bb78de 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWhatsThisClickedEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWhatsThisClickedEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWheelEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWheelEvent.cc index 6ee5d43c6..a23590de2 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWheelEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWheelEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWidget.cc index fdcfedbf1..907025b4d 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWidgetAction.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWidgetAction.cc index b5f498087..6ee16768c 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWidgetAction.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWidgetAction.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWidgetItem.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWidgetItem.cc index 9ba3ac1fd..4c7b06599 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWidgetItem.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWidgetItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWindowStateChangeEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWindowStateChangeEvent.cc index 9472f3229..a2d0498ed 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWindowStateChangeEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWindowStateChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWindowsStyle.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWindowsStyle.cc index 43ef0543e..44e262dd0 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWindowsStyle.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWindowsStyle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWizard.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWizard.cc index 5fc04f46b..86468b9d6 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWizard.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWizard.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWizardPage.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWizardPage.cc index 3619b117a..e3a95abb4 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWizardPage.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWizardPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQtGuiAdd.cc b/src/gsiqt/qt4/QtGui/gsiDeclQtGuiAdd.cc index 8a8d03c80..0342255e3 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQtGuiAdd.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQtGuiAdd.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQtGuiTypeTraits.h b/src/gsiqt/qt4/QtGui/gsiDeclQtGuiTypeTraits.h index e70833255..769c2f713 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQtGuiTypeTraits.h +++ b/src/gsiqt/qt4/QtGui/gsiDeclQtGuiTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtGui/gsiQtExternals.h b/src/gsiqt/qt4/QtGui/gsiQtExternals.h index 17bf60c09..7941e37ab 100644 --- a/src/gsiqt/qt4/QtGui/gsiQtExternals.h +++ b/src/gsiqt/qt4/QtGui/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQAbstractNetworkCache.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQAbstractNetworkCache.cc index 8a2618ed5..4d0bd5db6 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQAbstractNetworkCache.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQAbstractNetworkCache.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQAbstractSocket.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQAbstractSocket.cc index 6d7406fdf..8b251c17c 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQAbstractSocket.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQAbstractSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQAuthenticator.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQAuthenticator.cc index c8479763b..0e4cad3e3 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQAuthenticator.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQAuthenticator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQFtp.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQFtp.cc index 04501647a..1f6a725e8 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQFtp.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQFtp.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQHostAddress.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQHostAddress.cc index 9f6765613..eb1fe0e96 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQHostAddress.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQHostAddress.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQHostInfo.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQHostInfo.cc index d3e67186f..ce30c91a7 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQHostInfo.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQHostInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQIPv6Address.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQIPv6Address.cc index 8817d5bdc..ae71e4a56 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQIPv6Address.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQIPv6Address.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQLocalServer.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQLocalServer.cc index 8327b4147..df4ca93c0 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQLocalServer.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQLocalServer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQLocalSocket.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQLocalSocket.cc index 5caad6efb..949c3e9c6 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQLocalSocket.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQLocalSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkAccessManager.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkAccessManager.cc index 41bf918d1..d9f9dd3bb 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkAccessManager.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkAccessManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkAddressEntry.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkAddressEntry.cc index 94f59f41e..e45ed1f59 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkAddressEntry.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkAddressEntry.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCacheMetaData.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCacheMetaData.cc index 1433d9153..2898cbb27 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCacheMetaData.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCacheMetaData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCookie.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCookie.cc index 6e4ff5188..331ae89a1 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCookie.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCookie.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCookieJar.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCookieJar.cc index 0aec52671..eb4ae20db 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCookieJar.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkCookieJar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkDiskCache.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkDiskCache.cc index 8cdffc109..a4ababe3f 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkDiskCache.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkDiskCache.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkInterface.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkInterface.cc index ec76a5beb..b84e975b5 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkInterface.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxy.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxy.cc index 8c278099e..e8f77fa08 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxy.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxy.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxyFactory.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxyFactory.cc index edbdfc9c1..f9da09601 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxyFactory.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxyFactory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxyQuery.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxyQuery.cc index 47a8af350..c5ba25e63 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxyQuery.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkProxyQuery.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkReply.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkReply.cc index fbff77d92..5bbc3b8b5 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkReply.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkReply.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkRequest.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkRequest.cc index 77df592a5..7ac415486 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkRequest.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQNetworkRequest.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQSsl.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQSsl.cc index 05108c5a2..8324e12c6 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQSsl.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQSsl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslCertificate.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslCertificate.cc index 6429c1977..ffa7d9b18 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslCertificate.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslCertificate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslCipher.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslCipher.cc index ac02edb0c..bf80ccd21 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslCipher.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslCipher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslConfiguration.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslConfiguration.cc index 5a3ddac77..372a2cde1 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslConfiguration.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslConfiguration.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslError.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslError.cc index a1bd8dd12..d4d93a355 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslError.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslError.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslKey.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslKey.cc index 29f52c60c..409c70a4f 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslKey.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslKey.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslSocket.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslSocket.cc index 165028bb0..8a880b4f3 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQSslSocket.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQSslSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQTcpServer.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQTcpServer.cc index 908f4d64f..ccf182d6f 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQTcpServer.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQTcpServer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQTcpSocket.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQTcpSocket.cc index 14df55eb5..5b60f4326 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQTcpSocket.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQTcpSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQUdpSocket.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQUdpSocket.cc index b95756c5a..ca20cf7ba 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQUdpSocket.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQUdpSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQUrlInfo.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQUrlInfo.cc index c6b484658..229dc127d 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQUrlInfo.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQUrlInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQtNetworkAdd.cc b/src/gsiqt/qt4/QtNetwork/gsiDeclQtNetworkAdd.cc index a79a54d26..515ca75ff 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQtNetworkAdd.cc +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQtNetworkAdd.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiDeclQtNetworkTypeTraits.h b/src/gsiqt/qt4/QtNetwork/gsiDeclQtNetworkTypeTraits.h index 48c165629..940d63eb4 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiDeclQtNetworkTypeTraits.h +++ b/src/gsiqt/qt4/QtNetwork/gsiDeclQtNetworkTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtNetwork/gsiQtExternals.h b/src/gsiqt/qt4/QtNetwork/gsiQtExternals.h index 704560384..a46e516d0 100644 --- a/src/gsiqt/qt4/QtNetwork/gsiQtExternals.h +++ b/src/gsiqt/qt4/QtNetwork/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSql.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSql.cc index 45884dcba..fc131071b 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSql.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSql.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlDatabase.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlDatabase.cc index e36501a60..8c02032c0 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlDatabase.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlDatabase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlDriver.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlDriver.cc index eb52ffa03..1c07db255 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlDriver.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlDriver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlDriverCreatorBase.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlDriverCreatorBase.cc index eb9c51899..e23ed09e3 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlDriverCreatorBase.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlDriverCreatorBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlError.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlError.cc index 81b15afa7..cfb164816 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlError.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlError.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlField.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlField.cc index 067ef8af7..4608cef87 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlField.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlField.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlIndex.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlIndex.cc index f0c81eab8..79b7615c0 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlIndex.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlIndex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlQuery.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlQuery.cc index a6556d150..6791245d1 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlQuery.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlQuery.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlQueryModel.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlQueryModel.cc index ae030e924..4a14e7e2f 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlQueryModel.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlQueryModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlRecord.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlRecord.cc index 06217a116..3b7426f62 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlRecord.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlRelation.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlRelation.cc index 0051532e2..af5e99d73 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlRelation.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlRelation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlRelationalTableModel.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlRelationalTableModel.cc index 69150b8d6..dcb95811a 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlRelationalTableModel.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlRelationalTableModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlResult.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlResult.cc index bd27d8beb..92a163dea 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlResult.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlResult.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQSqlTableModel.cc b/src/gsiqt/qt4/QtSql/gsiDeclQSqlTableModel.cc index 99b00075a..fb31a9f4e 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQSqlTableModel.cc +++ b/src/gsiqt/qt4/QtSql/gsiDeclQSqlTableModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiDeclQtSqlTypeTraits.h b/src/gsiqt/qt4/QtSql/gsiDeclQtSqlTypeTraits.h index d57dfdae9..ef4073ff3 100644 --- a/src/gsiqt/qt4/QtSql/gsiDeclQtSqlTypeTraits.h +++ b/src/gsiqt/qt4/QtSql/gsiDeclQtSqlTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtSql/gsiQtExternals.h b/src/gsiqt/qt4/QtSql/gsiQtExternals.h index 68b366119..f16a80fda 100644 --- a/src/gsiqt/qt4/QtSql/gsiQtExternals.h +++ b/src/gsiqt/qt4/QtSql/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomAttr.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomAttr.cc index c10e55d17..42833bc24 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomAttr.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomAttr.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomCDATASection.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomCDATASection.cc index 28c0ed366..f37e2ed02 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomCDATASection.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomCDATASection.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomCharacterData.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomCharacterData.cc index 695130c61..29a0e04cf 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomCharacterData.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomCharacterData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomComment.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomComment.cc index b78d94aa0..175c92d5c 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomComment.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomComment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomDocument.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomDocument.cc index 6db140935..7993dc92d 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomDocument.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomDocument.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomDocumentFragment.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomDocumentFragment.cc index 09043e0a9..0ade67df3 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomDocumentFragment.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomDocumentFragment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomDocumentType.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomDocumentType.cc index 68d4967c4..c52458e9b 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomDocumentType.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomDocumentType.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomElement.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomElement.cc index 825eb0c95..b7cd58d76 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomElement.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomElement.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomEntity.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomEntity.cc index e1fbbb9cf..fb2c8a672 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomEntity.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomEntity.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomEntityReference.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomEntityReference.cc index 2659cfbcf..901420800 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomEntityReference.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomEntityReference.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomImplementation.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomImplementation.cc index 53ac02abb..34a8a4b5c 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomImplementation.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomImplementation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomNamedNodeMap.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomNamedNodeMap.cc index ad8c3d532..85d76eb0a 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomNamedNodeMap.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomNamedNodeMap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomNode.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomNode.cc index 69be9ecb4..10fae4d18 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomNode.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomNode.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomNodeList.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomNodeList.cc index f9cfe9ebf..5725b31f3 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomNodeList.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomNodeList.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomNotation.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomNotation.cc index e5350d0f5..deb27d9ad 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomNotation.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomNotation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomProcessingInstruction.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomProcessingInstruction.cc index 342a4e7cc..e87f787c0 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomProcessingInstruction.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomProcessingInstruction.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQDomText.cc b/src/gsiqt/qt4/QtXml/gsiDeclQDomText.cc index 0cc0bfa51..4dfad040e 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQDomText.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQDomText.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlAttributes.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlAttributes.cc index e583beee4..d5cec2f2c 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlAttributes.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlAttributes.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlContentHandler.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlContentHandler.cc index 1322fe3d6..ba970f86d 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlContentHandler.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlContentHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlDTDHandler.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlDTDHandler.cc index 4281bafc1..1f7c04cd4 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlDTDHandler.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlDTDHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlDeclHandler.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlDeclHandler.cc index 8d65bfa7f..ddc20798c 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlDeclHandler.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlDeclHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlDefaultHandler.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlDefaultHandler.cc index 8f0f6158b..525f02749 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlDefaultHandler.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlDefaultHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlEntityResolver.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlEntityResolver.cc index 83462f189..d3b164445 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlEntityResolver.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlEntityResolver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlErrorHandler.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlErrorHandler.cc index a60b5fa1f..9a8746b16 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlErrorHandler.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlErrorHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlInputSource.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlInputSource.cc index 2a360d264..1a7f90a7d 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlInputSource.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlInputSource.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlLexicalHandler.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlLexicalHandler.cc index da9112317..b3741ac51 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlLexicalHandler.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlLexicalHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlLocator.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlLocator.cc index 52084ecec..de37d7a3d 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlLocator.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlLocator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlNamespaceSupport.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlNamespaceSupport.cc index dea5d7cf9..8464efcee 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlNamespaceSupport.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlNamespaceSupport.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlParseException.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlParseException.cc index 093ee25a5..7da965ba9 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlParseException.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlParseException.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlReader.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlReader.cc index bca2e26ee..5d922113f 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlReader.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlSimpleReader.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlSimpleReader.cc index 024d5f713..72a3dee4c 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlSimpleReader.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlSimpleReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQtXmlTypeTraits.h b/src/gsiqt/qt4/QtXml/gsiDeclQtXmlTypeTraits.h index 08e08b0dc..b65c94328 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQtXmlTypeTraits.h +++ b/src/gsiqt/qt4/QtXml/gsiDeclQtXmlTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt4/QtXml/gsiQtExternals.h b/src/gsiqt/qt4/QtXml/gsiQtExternals.h index 0a2223e04..a9519663a 100644 --- a/src/gsiqt/qt4/QtXml/gsiQtExternals.h +++ b/src/gsiqt/qt4/QtXml/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractAnimation.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractAnimation.cc index 9542eff3d..8b19059f6 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractAnimation.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractAnimation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractEventDispatcher.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractEventDispatcher.cc index ed592020e..c334cf669 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractEventDispatcher.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractEventDispatcher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractEventDispatcher_TimerInfo.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractEventDispatcher_TimerInfo.cc index 4d488dcdc..642fe5a93 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractEventDispatcher_TimerInfo.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractEventDispatcher_TimerInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractItemModel.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractItemModel.cc index 69e1729b8..116ade41d 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractItemModel.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractItemModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractListModel.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractListModel.cc index 92f67ee02..689f90b53 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractListModel.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractListModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractNativeEventFilter.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractNativeEventFilter.cc index b243ca95f..fc5e670e2 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractNativeEventFilter.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractNativeEventFilter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractProxyModel.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractProxyModel.cc index 07ba2ef73..e50738481 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractProxyModel.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractProxyModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractState.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractState.cc index 2442ced77..a5bc9346b 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractState.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractState.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractTableModel.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractTableModel.cc index 055dad027..43c6519c3 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractTableModel.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractTableModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractTransition.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractTransition.cc index 53cd654ab..ab5f20563 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAbstractTransition.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAbstractTransition.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAnimationDriver.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAnimationDriver.cc index 9afa105f0..7686982c7 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAnimationDriver.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAnimationDriver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAnimationGroup.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAnimationGroup.cc index eb7b65918..a75fe9d01 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAnimationGroup.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAnimationGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQAssociativeIterable.cc b/src/gsiqt/qt5/QtCore/gsiDeclQAssociativeIterable.cc index c2fa99e8b..203aec187 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQAssociativeIterable.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQAssociativeIterable.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQBasicMutex.cc b/src/gsiqt/qt5/QtCore/gsiDeclQBasicMutex.cc index ed6c20b06..70dd6b35f 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQBasicMutex.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQBasicMutex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQBasicTimer.cc b/src/gsiqt/qt5/QtCore/gsiDeclQBasicTimer.cc index fb7645ea7..677b18e2a 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQBasicTimer.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQBasicTimer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQBuffer.cc b/src/gsiqt/qt5/QtCore/gsiDeclQBuffer.cc index 4fe48495e..c090e57b9 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQBuffer.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQBuffer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQByteArrayDataPtr.cc b/src/gsiqt/qt5/QtCore/gsiDeclQByteArrayDataPtr.cc index b1be8e541..62be847c5 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQByteArrayDataPtr.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQByteArrayDataPtr.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQByteArrayMatcher.cc b/src/gsiqt/qt5/QtCore/gsiDeclQByteArrayMatcher.cc index ccc6bb207..7a9f5b12f 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQByteArrayMatcher.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQByteArrayMatcher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQChildEvent.cc b/src/gsiqt/qt5/QtCore/gsiDeclQChildEvent.cc index cc6e1a5d0..0f74e24e3 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQChildEvent.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQChildEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQCollator.cc b/src/gsiqt/qt5/QtCore/gsiDeclQCollator.cc index c60bddb49..c04453fae 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQCollator.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQCollator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQCollatorSortKey.cc b/src/gsiqt/qt5/QtCore/gsiDeclQCollatorSortKey.cc index 5e4af3079..7da0db756 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQCollatorSortKey.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQCollatorSortKey.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQCommandLineOption.cc b/src/gsiqt/qt5/QtCore/gsiDeclQCommandLineOption.cc index 47ad9d332..b56d59ace 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQCommandLineOption.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQCommandLineOption.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQCommandLineParser.cc b/src/gsiqt/qt5/QtCore/gsiDeclQCommandLineParser.cc index a7a7c49e2..e8b65ff9c 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQCommandLineParser.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQCommandLineParser.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQCoreApplication.cc b/src/gsiqt/qt5/QtCore/gsiDeclQCoreApplication.cc index a4aeafa73..f353989b6 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQCoreApplication.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQCoreApplication.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQCryptographicHash.cc b/src/gsiqt/qt5/QtCore/gsiDeclQCryptographicHash.cc index 27133e014..a7b3668a6 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQCryptographicHash.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQCryptographicHash.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQDataStream.cc b/src/gsiqt/qt5/QtCore/gsiDeclQDataStream.cc index 02f4cf46b..8f4990e73 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQDataStream.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQDataStream.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQDate.cc b/src/gsiqt/qt5/QtCore/gsiDeclQDate.cc index 4c81fd3c2..a0d239070 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQDate.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQDate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQDateTime.cc b/src/gsiqt/qt5/QtCore/gsiDeclQDateTime.cc index b201384ae..31ce607e7 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQDateTime.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQDateTime.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQDebug.cc b/src/gsiqt/qt5/QtCore/gsiDeclQDebug.cc index 247eba0ae..145fc621f 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQDebug.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQDebug.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQDebugStateSaver.cc b/src/gsiqt/qt5/QtCore/gsiDeclQDebugStateSaver.cc index 86a4fb7f0..3dc5d270a 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQDebugStateSaver.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQDebugStateSaver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQDeferredDeleteEvent.cc b/src/gsiqt/qt5/QtCore/gsiDeclQDeferredDeleteEvent.cc index cb65dd3dc..cacbec8a5 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQDeferredDeleteEvent.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQDeferredDeleteEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQDir.cc b/src/gsiqt/qt5/QtCore/gsiDeclQDir.cc index 2439dbb39..87275562d 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQDir.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQDir.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQDirIterator.cc b/src/gsiqt/qt5/QtCore/gsiDeclQDirIterator.cc index c33e5e0c7..e07c011db 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQDirIterator.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQDirIterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQDynamicPropertyChangeEvent.cc b/src/gsiqt/qt5/QtCore/gsiDeclQDynamicPropertyChangeEvent.cc index 504153c86..840ff6eb5 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQDynamicPropertyChangeEvent.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQDynamicPropertyChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQEasingCurve.cc b/src/gsiqt/qt5/QtCore/gsiDeclQEasingCurve.cc index f55747c2f..a4936d388 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQEasingCurve.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQEasingCurve.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQElapsedTimer.cc b/src/gsiqt/qt5/QtCore/gsiDeclQElapsedTimer.cc index a80777c3e..4f90f1763 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQElapsedTimer.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQElapsedTimer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQEvent.cc b/src/gsiqt/qt5/QtCore/gsiDeclQEvent.cc index 8162f0bd2..d92e03f3e 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQEvent.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQEventLoop.cc b/src/gsiqt/qt5/QtCore/gsiDeclQEventLoop.cc index 5fbd0132b..83b335cd9 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQEventLoop.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQEventLoop.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQEventLoopLocker.cc b/src/gsiqt/qt5/QtCore/gsiDeclQEventLoopLocker.cc index a7d8a5571..9e3238317 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQEventLoopLocker.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQEventLoopLocker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQEventTransition.cc b/src/gsiqt/qt5/QtCore/gsiDeclQEventTransition.cc index fd4c3edac..255e30464 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQEventTransition.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQEventTransition.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQFactoryInterface.cc b/src/gsiqt/qt5/QtCore/gsiDeclQFactoryInterface.cc index 537af1bc8..326b8d6ed 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQFactoryInterface.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQFactoryInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQFile.cc b/src/gsiqt/qt5/QtCore/gsiDeclQFile.cc index d49757f79..43e94d1e6 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQFile.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQFile.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQFileDevice.cc b/src/gsiqt/qt5/QtCore/gsiDeclQFileDevice.cc index c1d0e5d32..a83c40ce2 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQFileDevice.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQFileDevice.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQFileInfo.cc b/src/gsiqt/qt5/QtCore/gsiDeclQFileInfo.cc index f95ac389e..1573dacbe 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQFileInfo.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQFileInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQFileSelector.cc b/src/gsiqt/qt5/QtCore/gsiDeclQFileSelector.cc index 3b8f13210..bdcc7cf3c 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQFileSelector.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQFileSelector.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQFileSystemWatcher.cc b/src/gsiqt/qt5/QtCore/gsiDeclQFileSystemWatcher.cc index 94e32fac4..4a6013a48 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQFileSystemWatcher.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQFileSystemWatcher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQFinalState.cc b/src/gsiqt/qt5/QtCore/gsiDeclQFinalState.cc index 4a190db78..5b20580bb 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQFinalState.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQFinalState.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQHistoryState.cc b/src/gsiqt/qt5/QtCore/gsiDeclQHistoryState.cc index 5c95946ad..65a4217c9 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQHistoryState.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQHistoryState.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQIODevice.cc b/src/gsiqt/qt5/QtCore/gsiDeclQIODevice.cc index 9b8891511..d7ac3faf2 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQIODevice.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQIODevice.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQIdentityProxyModel.cc b/src/gsiqt/qt5/QtCore/gsiDeclQIdentityProxyModel.cc index 00cfe0709..15d42aee3 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQIdentityProxyModel.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQIdentityProxyModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQItemSelection.cc b/src/gsiqt/qt5/QtCore/gsiDeclQItemSelection.cc index ed5731174..68b617c24 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQItemSelection.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQItemSelection.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQItemSelectionModel.cc b/src/gsiqt/qt5/QtCore/gsiDeclQItemSelectionModel.cc index 2e24e837e..f1d768bb3 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQItemSelectionModel.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQItemSelectionModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQItemSelectionRange.cc b/src/gsiqt/qt5/QtCore/gsiDeclQItemSelectionRange.cc index 4e66dff0f..04a3bd2a0 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQItemSelectionRange.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQItemSelectionRange.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray.cc index e9e5b24fb..04c598db0 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray_Const_iterator.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray_Const_iterator.cc index 27aede5d3..4897da4cb 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray_Const_iterator.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray_Const_iterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray_Iterator.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray_Iterator.cc index 2a7090269..9c1fe7238 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray_Iterator.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonArray_Iterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonDocument.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonDocument.cc index d33b82a8b..5e8ca7b31 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonDocument.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonDocument.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject.cc index 534d30dbe..58bf9f999 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject_Const_iterator.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject_Const_iterator.cc index 85e918ccc..d6605b254 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject_Const_iterator.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject_Const_iterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject_Iterator.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject_Iterator.cc index 1249e212c..a951206a7 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject_Iterator.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonObject_Iterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonParseError.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonParseError.cc index 181abf145..79fbbc1d7 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonParseError.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonParseError.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonValue.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonValue.cc index 334f685c2..363d74492 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonValue.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonValue.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonValuePtr.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonValuePtr.cc index 80d307b5c..873896244 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonValuePtr.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonValuePtr.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonValueRef.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonValueRef.cc index f4a1b3a35..73b4fb635 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonValueRef.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonValueRef.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQJsonValueRefPtr.cc b/src/gsiqt/qt5/QtCore/gsiDeclQJsonValueRefPtr.cc index c6eadde61..9fb74b168 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQJsonValueRefPtr.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQJsonValueRefPtr.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQLibrary.cc b/src/gsiqt/qt5/QtCore/gsiDeclQLibrary.cc index 7456a4028..66962cf3d 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQLibrary.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQLibrary.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQLibraryInfo.cc b/src/gsiqt/qt5/QtCore/gsiDeclQLibraryInfo.cc index 07a2537a0..7b373576c 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQLibraryInfo.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQLibraryInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQLine.cc b/src/gsiqt/qt5/QtCore/gsiDeclQLine.cc index 526f4ed70..cec38e5ae 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQLine.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQLine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQLineF.cc b/src/gsiqt/qt5/QtCore/gsiDeclQLineF.cc index f70aa6483..6426b5c9d 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQLineF.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQLineF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQLocale.cc b/src/gsiqt/qt5/QtCore/gsiDeclQLocale.cc index 5e68c0ec9..1031cefba 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQLocale.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQLocale.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQLockFile.cc b/src/gsiqt/qt5/QtCore/gsiDeclQLockFile.cc index cead0ec68..042df988e 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQLockFile.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQLockFile.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQLoggingCategory.cc b/src/gsiqt/qt5/QtCore/gsiDeclQLoggingCategory.cc index 34de78f95..4e6af9fb7 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQLoggingCategory.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQLoggingCategory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMapDataBase.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMapDataBase.cc index 60d275425..63427b577 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMapDataBase.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMapDataBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMapNodeBase.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMapNodeBase.cc index 7d6c2ab03..44a3aed4d 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMapNodeBase.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMapNodeBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMargins.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMargins.cc index 346e75806..8e75bd13e 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMargins.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMargins.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMarginsF.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMarginsF.cc index a156b4072..22d98a4a6 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMarginsF.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMarginsF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMessageAuthenticationCode.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMessageAuthenticationCode.cc index 3038f7b68..55dda76c0 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMessageAuthenticationCode.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMessageAuthenticationCode.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMessageLogContext.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMessageLogContext.cc index dffb11145..d8a31cf1f 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMessageLogContext.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMessageLogContext.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMessageLogger.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMessageLogger.cc index 93b55ca26..188bc1944 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMessageLogger.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMessageLogger.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMetaClassInfo.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMetaClassInfo.cc index 2dcf39d8e..1394fe529 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMetaClassInfo.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMetaClassInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMetaEnum.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMetaEnum.cc index 342b33d55..3dc5d0f28 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMetaEnum.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMetaEnum.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMetaMethod.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMetaMethod.cc index 515a83e60..893a2082c 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMetaMethod.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMetaMethod.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMetaObject.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMetaObject.cc index f4bd12576..8fbca4659 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMetaObject.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMetaObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMetaObject_Connection.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMetaObject_Connection.cc index de4d22191..93d9605cd 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMetaObject_Connection.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMetaObject_Connection.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMetaProperty.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMetaProperty.cc index afe220d14..b12d7a5ca 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMetaProperty.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMetaProperty.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMimeData.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMimeData.cc index 14652ed34..8f3ea0d68 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMimeData.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMimeData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMimeDatabase.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMimeDatabase.cc index b6cde5439..5e5d2f621 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMimeDatabase.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMimeDatabase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMimeType.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMimeType.cc index ceb8e70b1..1b89b0f8e 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMimeType.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMimeType.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQModelIndex.cc b/src/gsiqt/qt5/QtCore/gsiDeclQModelIndex.cc index d64cf5422..277375812 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQModelIndex.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQModelIndex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQMutex.cc b/src/gsiqt/qt5/QtCore/gsiDeclQMutex.cc index ac4df5b72..a7051a84b 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQMutex.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQMutex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQNoDebug.cc b/src/gsiqt/qt5/QtCore/gsiDeclQNoDebug.cc index 1dd8dc938..7b7910e89 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQNoDebug.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQNoDebug.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQObject.cc b/src/gsiqt/qt5/QtCore/gsiDeclQObject.cc index 1c815ca8d..3dd887ee4 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQObject.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQParallelAnimationGroup.cc b/src/gsiqt/qt5/QtCore/gsiDeclQParallelAnimationGroup.cc index 9455ee184..04b2e4f5f 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQParallelAnimationGroup.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQParallelAnimationGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQPauseAnimation.cc b/src/gsiqt/qt5/QtCore/gsiDeclQPauseAnimation.cc index 8401c758d..9e3b572aa 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQPauseAnimation.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQPauseAnimation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQPersistentModelIndex.cc b/src/gsiqt/qt5/QtCore/gsiDeclQPersistentModelIndex.cc index a012e9e79..841eb67ea 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQPersistentModelIndex.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQPersistentModelIndex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQPluginLoader.cc b/src/gsiqt/qt5/QtCore/gsiDeclQPluginLoader.cc index 1ea77e096..4dfda126c 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQPluginLoader.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQPluginLoader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQPoint.cc b/src/gsiqt/qt5/QtCore/gsiDeclQPoint.cc index 6b1faeec7..bc8d66296 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQPoint.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQPoint.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQPointF.cc b/src/gsiqt/qt5/QtCore/gsiDeclQPointF.cc index af71db109..5b683f515 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQPointF.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQPointF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQProcess.cc b/src/gsiqt/qt5/QtCore/gsiDeclQProcess.cc index 512183b62..614bd05e5 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQProcess.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQProcess.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQProcessEnvironment.cc b/src/gsiqt/qt5/QtCore/gsiDeclQProcessEnvironment.cc index c0a81f358..75546e703 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQProcessEnvironment.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQProcessEnvironment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQPropertyAnimation.cc b/src/gsiqt/qt5/QtCore/gsiDeclQPropertyAnimation.cc index 5902fc65f..2816a6ba0 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQPropertyAnimation.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQPropertyAnimation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQReadLocker.cc b/src/gsiqt/qt5/QtCore/gsiDeclQReadLocker.cc index e1c03673e..ab9873e02 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQReadLocker.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQReadLocker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQReadWriteLock.cc b/src/gsiqt/qt5/QtCore/gsiDeclQReadWriteLock.cc index 6482639d8..9bdba3b2b 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQReadWriteLock.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQReadWriteLock.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQRect.cc b/src/gsiqt/qt5/QtCore/gsiDeclQRect.cc index ae3369430..5e25dd7b8 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQRect.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQRect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQRectF.cc b/src/gsiqt/qt5/QtCore/gsiDeclQRectF.cc index 5021441d4..b4ab787d2 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQRectF.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQRectF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQRegExp.cc b/src/gsiqt/qt5/QtCore/gsiDeclQRegExp.cc index aa6aadd94..74a97b3e5 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQRegExp.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQRegExp.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpression.cc b/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpression.cc index ab28fe5e9..c65cc4ac2 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpression.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpression.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpressionMatch.cc b/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpressionMatch.cc index 9ac7a8d5a..4b8904c79 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpressionMatch.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpressionMatch.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpressionMatchIterator.cc b/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpressionMatchIterator.cc index f59ec1f86..8d3c1847b 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpressionMatchIterator.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQRegularExpressionMatchIterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQResource.cc b/src/gsiqt/qt5/QtCore/gsiDeclQResource.cc index 93e7f7b4e..0b155ee8d 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQResource.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQResource.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQRunnable.cc b/src/gsiqt/qt5/QtCore/gsiDeclQRunnable.cc index fa611f969..a9b5a9b69 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQRunnable.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQRunnable.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSaveFile.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSaveFile.cc index 43ade78a2..3b876f781 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSaveFile.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSaveFile.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSemaphore.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSemaphore.cc index 882319bcf..cee3bac17 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSemaphore.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSemaphore.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSequentialAnimationGroup.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSequentialAnimationGroup.cc index deef1e4c8..0e5973760 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSequentialAnimationGroup.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSequentialAnimationGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSequentialIterable.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSequentialIterable.cc index 5f1562d6f..742a542f0 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSequentialIterable.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSequentialIterable.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSettings.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSettings.cc index 08777063e..68016c2bc 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSettings.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSettings.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSharedMemory.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSharedMemory.cc index 1a40f1a29..7264524db 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSharedMemory.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSharedMemory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSignalBlocker.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSignalBlocker.cc index 64f191723..0b1110fe3 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSignalBlocker.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSignalBlocker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSignalMapper.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSignalMapper.cc index 03e063bf5..9f0b03c57 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSignalMapper.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSignalMapper.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSignalTransition.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSignalTransition.cc index a39e67fa9..d97f9b5d4 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSignalTransition.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSignalTransition.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSize.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSize.cc index 400bc1b54..f1592c977 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSize.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSize.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSizeF.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSizeF.cc index 7cc3a79ac..f6f89eeae 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSizeF.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSizeF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSocketNotifier.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSocketNotifier.cc index 17df8e963..283683e5b 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSocketNotifier.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSocketNotifier.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSortFilterProxyModel.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSortFilterProxyModel.cc index 307066fe2..9ef90f660 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSortFilterProxyModel.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSortFilterProxyModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQStandardPaths.cc b/src/gsiqt/qt5/QtCore/gsiDeclQStandardPaths.cc index b549eeaed..2349e000f 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQStandardPaths.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQStandardPaths.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQState.cc b/src/gsiqt/qt5/QtCore/gsiDeclQState.cc index b5aee1ae3..bc4c049a2 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQState.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQState.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine.cc b/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine.cc index 831187e6c..c6a40b4ac 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine_SignalEvent.cc b/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine_SignalEvent.cc index 80245f793..f28c0f2fe 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine_SignalEvent.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine_SignalEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine_WrappedEvent.cc b/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine_WrappedEvent.cc index 70a0ef0cb..2c4f624b3 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine_WrappedEvent.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQStateMachine_WrappedEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQStaticPlugin.cc b/src/gsiqt/qt5/QtCore/gsiDeclQStaticPlugin.cc index 5cad96d0a..67863ac13 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQStaticPlugin.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQStaticPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQStorageInfo.cc b/src/gsiqt/qt5/QtCore/gsiDeclQStorageInfo.cc index 83a54a072..295ae5243 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQStorageInfo.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQStorageInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQStringDataPtr.cc b/src/gsiqt/qt5/QtCore/gsiDeclQStringDataPtr.cc index 83aae202a..d81c8c536 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQStringDataPtr.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQStringDataPtr.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQStringListModel.cc b/src/gsiqt/qt5/QtCore/gsiDeclQStringListModel.cc index 1799d6034..85b02bce0 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQStringListModel.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQStringListModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQStringMatcher.cc b/src/gsiqt/qt5/QtCore/gsiDeclQStringMatcher.cc index d801a2d54..b3c806ba9 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQStringMatcher.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQStringMatcher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSysInfo.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSysInfo.cc index 924962189..d6ac2e66f 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSysInfo.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSysInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQSystemSemaphore.cc b/src/gsiqt/qt5/QtCore/gsiDeclQSystemSemaphore.cc index f28931313..89e079d69 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQSystemSemaphore.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQSystemSemaphore.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTemporaryDir.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTemporaryDir.cc index 63f8c2393..4da10bb50 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTemporaryDir.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTemporaryDir.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTemporaryFile.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTemporaryFile.cc index 517f26d4b..00a9fc0e7 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTemporaryFile.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTemporaryFile.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTextBoundaryFinder.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTextBoundaryFinder.cc index 583225e37..9ac3ec0b8 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTextBoundaryFinder.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTextBoundaryFinder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTextCodec.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTextCodec.cc index 7111e24a0..4a305ae80 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTextCodec.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTextCodec.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTextCodec_ConverterState.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTextCodec_ConverterState.cc index b5bfea6fc..c5aadee02 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTextCodec_ConverterState.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTextCodec_ConverterState.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTextDecoder.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTextDecoder.cc index 18ca949be..6bb6e8529 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTextDecoder.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTextDecoder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTextEncoder.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTextEncoder.cc index e99feaa13..11d73fd38 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTextEncoder.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTextEncoder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTextStream.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTextStream.cc index b22862215..de7b0002c 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTextStream.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTextStream.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQThread.cc b/src/gsiqt/qt5/QtCore/gsiDeclQThread.cc index 66fbee8ba..99a4bbcce 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQThread.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQThread.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQThreadPool.cc b/src/gsiqt/qt5/QtCore/gsiDeclQThreadPool.cc index 79d50b2a5..9da131df8 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQThreadPool.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQThreadPool.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTime.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTime.cc index 287f1f129..597877cd2 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTime.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTime.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTimeLine.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTimeLine.cc index e35ff0a88..1827ed29f 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTimeLine.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTimeLine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTimeZone.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTimeZone.cc index 007c55977..5ec23ac47 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTimeZone.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTimeZone.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTimeZone_OffsetData.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTimeZone_OffsetData.cc index 9f10f9195..4d8aae1d0 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTimeZone_OffsetData.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTimeZone_OffsetData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTimer.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTimer.cc index 38870d910..38116d1b9 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTimer.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTimer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTimerEvent.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTimerEvent.cc index a676b0f04..7ea80e964 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTimerEvent.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTimerEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQTranslator.cc b/src/gsiqt/qt5/QtCore/gsiDeclQTranslator.cc index 8a3fcc278..aee2d4c61 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQTranslator.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQTranslator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQUrl.cc b/src/gsiqt/qt5/QtCore/gsiDeclQUrl.cc index 3b06648d4..1f0c4e5a4 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQUrl.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQUrl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQUrlQuery.cc b/src/gsiqt/qt5/QtCore/gsiDeclQUrlQuery.cc index 049437882..06f2a802e 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQUrlQuery.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQUrlQuery.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQVariantAnimation.cc b/src/gsiqt/qt5/QtCore/gsiDeclQVariantAnimation.cc index 49577cf1b..6823b553e 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQVariantAnimation.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQVariantAnimation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQWaitCondition.cc b/src/gsiqt/qt5/QtCore/gsiDeclQWaitCondition.cc index 0e5d37a7c..4ab9503de 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQWaitCondition.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQWaitCondition.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQWriteLocker.cc b/src/gsiqt/qt5/QtCore/gsiDeclQWriteLocker.cc index 561062a86..d151177e7 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQWriteLocker.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQWriteLocker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamAttribute.cc b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamAttribute.cc index 6619f45aa..5c9b051c2 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamAttribute.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamAttribute.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamAttributes.cc b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamAttributes.cc index 0adbdcdca..d32f6e12a 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamAttributes.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamAttributes.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamEntityDeclaration.cc b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamEntityDeclaration.cc index 8ede02b9c..79b24f65d 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamEntityDeclaration.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamEntityDeclaration.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamEntityResolver.cc b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamEntityResolver.cc index 03f40b17d..f9b30acc7 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamEntityResolver.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamEntityResolver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamNamespaceDeclaration.cc b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamNamespaceDeclaration.cc index 43d944f51..1bcf6f576 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamNamespaceDeclaration.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamNamespaceDeclaration.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamNotationDeclaration.cc b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamNotationDeclaration.cc index 8820133e1..d6761c9b9 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamNotationDeclaration.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamNotationDeclaration.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamReader.cc b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamReader.cc index 4a075882d..03afa318a 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamReader.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamStringRef.cc b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamStringRef.cc index 62fa33a93..9a81da21d 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamStringRef.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamStringRef.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamWriter.cc b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamWriter.cc index a93de5bbf..d107d0103 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamWriter.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQXmlStreamWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQt.cc b/src/gsiqt/qt5/QtCore/gsiDeclQt.cc index e4c4a5edf..6479d6821 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQt.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQtCoreAdd.cc b/src/gsiqt/qt5/QtCore/gsiDeclQtCoreAdd.cc index a81af0ebd..c2c07f57e 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQtCoreAdd.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQtCoreAdd.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQtCoreTypeTraits.h b/src/gsiqt/qt5/QtCore/gsiDeclQtCoreTypeTraits.h index 849752e7f..3922ad2a9 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQtCoreTypeTraits.h +++ b/src/gsiqt/qt5/QtCore/gsiDeclQtCoreTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQt_1.cc b/src/gsiqt/qt5/QtCore/gsiDeclQt_1.cc index a68554827..c22c1db5d 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQt_1.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQt_1.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQt_2.cc b/src/gsiqt/qt5/QtCore/gsiDeclQt_2.cc index fb933fb0e..6c05553bf 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQt_2.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQt_2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQt_3.cc b/src/gsiqt/qt5/QtCore/gsiDeclQt_3.cc index e3140989b..073307a53 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQt_3.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQt_3.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQt_4.cc b/src/gsiqt/qt5/QtCore/gsiDeclQt_4.cc index 713c64699..1bab4233a 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQt_4.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQt_4.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtCore/gsiQtExternals.h b/src/gsiqt/qt5/QtCore/gsiQtExternals.h index 52b63d1a9..a4752cf63 100644 --- a/src/gsiqt/qt5/QtCore/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtCore/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractExtensionFactory.cc b/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractExtensionFactory.cc index e86956c7c..28d8318c9 100644 --- a/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractExtensionFactory.cc +++ b/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractExtensionFactory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractExtensionManager.cc b/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractExtensionManager.cc index 293cc4323..fd9fef2ab 100644 --- a/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractExtensionManager.cc +++ b/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractExtensionManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractFormBuilder.cc b/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractFormBuilder.cc index b0b0ec104..2f29276bb 100644 --- a/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractFormBuilder.cc +++ b/src/gsiqt/qt5/QtDesigner/gsiDeclQAbstractFormBuilder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtDesigner/gsiDeclQFormBuilder.cc b/src/gsiqt/qt5/QtDesigner/gsiDeclQFormBuilder.cc index f5fea2a53..059f3d742 100644 --- a/src/gsiqt/qt5/QtDesigner/gsiDeclQFormBuilder.cc +++ b/src/gsiqt/qt5/QtDesigner/gsiDeclQFormBuilder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtDesigner/gsiDeclQtDesignerTypeTraits.h b/src/gsiqt/qt5/QtDesigner/gsiDeclQtDesignerTypeTraits.h index 1847964c0..5fcb28614 100644 --- a/src/gsiqt/qt5/QtDesigner/gsiDeclQtDesignerTypeTraits.h +++ b/src/gsiqt/qt5/QtDesigner/gsiDeclQtDesignerTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtDesigner/gsiQtExternals.h b/src/gsiqt/qt5/QtDesigner/gsiQtExternals.h index d22bfdd17..689540ad6 100644 --- a/src/gsiqt/qt5/QtDesigner/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtDesigner/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout.cc index 3804bf2a6..c0b6c2810 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout_PaintContext.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout_PaintContext.cc index ed7b06755..7f297826d 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout_PaintContext.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout_PaintContext.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout_Selection.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout_Selection.cc index 6011bc485..af4e89189 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout_Selection.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAbstractTextDocumentLayout_Selection.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAbstractUndoItem.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAbstractUndoItem.cc index 50b2ffa55..eb36b0bd1 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAbstractUndoItem.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAbstractUndoItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessible.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessible.cc index c75d211f9..d45c51c4c 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessible.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessible.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleActionInterface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleActionInterface.cc index 1e5418464..4fe124efe 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleActionInterface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleActionInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleEditableTextInterface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleEditableTextInterface.cc index e6c71ac90..f01c51a03 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleEditableTextInterface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleEditableTextInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleEvent.cc index 928afce77..af1571430 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleImageInterface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleImageInterface.cc index 64040a9a7..ec66ccb97 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleImageInterface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleImageInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleInterface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleInterface.cc index 12a58d601..a8b163731 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleInterface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleObject.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleObject.cc index a42ba875b..25b9f94ec 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleObject.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleStateChangeEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleStateChangeEvent.cc index 630447b29..2f21d4185 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleStateChangeEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleStateChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableCellInterface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableCellInterface.cc index a07c26c12..e9b06051f 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableCellInterface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableCellInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableInterface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableInterface.cc index 47e74b225..0bb650b36 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableInterface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableModelChangeEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableModelChangeEvent.cc index 92bd156ff..03dbb7bd4 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableModelChangeEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTableModelChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextCursorEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextCursorEvent.cc index b025583e9..a9b3d7665 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextCursorEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextCursorEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextInsertEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextInsertEvent.cc index b1c866907..66b7c40e0 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextInsertEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextInsertEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextInterface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextInterface.cc index 95bd92306..eb141c21d 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextInterface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextRemoveEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextRemoveEvent.cc index 996f480af..e5da570a9 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextRemoveEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextRemoveEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextSelectionEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextSelectionEvent.cc index 453101b90..1659ecdeb 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextSelectionEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextSelectionEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextUpdateEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextUpdateEvent.cc index b94053688..ea99e620b 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextUpdateEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleTextUpdateEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleValueChangeEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleValueChangeEvent.cc index 324c76f05..0a339aca3 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleValueChangeEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleValueChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleValueInterface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleValueInterface.cc index 52dae2359..513d9bf85 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleValueInterface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessibleValueInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessible_ActivationObserver.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessible_ActivationObserver.cc index afe0e1270..553841fbc 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessible_ActivationObserver.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessible_ActivationObserver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQAccessible_State.cc b/src/gsiqt/qt5/QtGui/gsiDeclQAccessible_State.cc index a6a4440fd..b18e68cbe 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQAccessible_State.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQAccessible_State.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQActionEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQActionEvent.cc index 67f712faf..1350a9225 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQActionEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQActionEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQApplicationStateChangeEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQApplicationStateChangeEvent.cc index 85a175708..4606a3276 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQApplicationStateChangeEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQApplicationStateChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQBackingStore.cc b/src/gsiqt/qt5/QtGui/gsiDeclQBackingStore.cc index 7375863d4..8beeaeac5 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQBackingStore.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQBackingStore.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQBitmap.cc b/src/gsiqt/qt5/QtGui/gsiDeclQBitmap.cc index 38a79005c..3af2ace4a 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQBitmap.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQBitmap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQBrush.cc b/src/gsiqt/qt5/QtGui/gsiDeclQBrush.cc index c93eb18e0..0c5610989 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQBrush.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQBrush.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQClipboard.cc b/src/gsiqt/qt5/QtGui/gsiDeclQClipboard.cc index d78a6b7e8..cd3c1c236 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQClipboard.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQClipboard.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQCloseEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQCloseEvent.cc index c3f301847..ffb9e2ec4 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQCloseEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQCloseEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQColor.cc b/src/gsiqt/qt5/QtGui/gsiDeclQColor.cc index 240a13a53..c40ca2794 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQColor.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQColor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQConicalGradient.cc b/src/gsiqt/qt5/QtGui/gsiDeclQConicalGradient.cc index c1e320a2d..ab0abbb21 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQConicalGradient.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQConicalGradient.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQContextMenuEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQContextMenuEvent.cc index 0e59d168e..eef3322ff 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQContextMenuEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQContextMenuEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQCursor.cc b/src/gsiqt/qt5/QtGui/gsiDeclQCursor.cc index 659cff9ae..8ac1f622c 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQCursor.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQCursor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQDesktopServices.cc b/src/gsiqt/qt5/QtGui/gsiDeclQDesktopServices.cc index 52d172672..4016dbebf 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQDesktopServices.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQDesktopServices.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQDoubleValidator.cc b/src/gsiqt/qt5/QtGui/gsiDeclQDoubleValidator.cc index 1d1b5fb91..cb82ee032 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQDoubleValidator.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQDoubleValidator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQDrag.cc b/src/gsiqt/qt5/QtGui/gsiDeclQDrag.cc index 80037470a..65a17955e 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQDrag.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQDrag.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQDragEnterEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQDragEnterEvent.cc index 4983c8980..4ccfd4e2d 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQDragEnterEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQDragEnterEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQDragLeaveEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQDragLeaveEvent.cc index 29931bb51..85544bb71 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQDragLeaveEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQDragLeaveEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQDragMoveEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQDragMoveEvent.cc index df39e464d..1a4390992 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQDragMoveEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQDragMoveEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQDropEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQDropEvent.cc index ac487bf13..7a8ee33b7 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQDropEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQDropEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQEnterEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQEnterEvent.cc index a7cd6e834..5d4186a29 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQEnterEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQEnterEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQExposeEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQExposeEvent.cc index ebe6ef0c6..0ec4b6880 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQExposeEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQExposeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQFileOpenEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQFileOpenEvent.cc index 44eb0edca..ccf75580c 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQFileOpenEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQFileOpenEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQFocusEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQFocusEvent.cc index f53bd35f5..e6610468b 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQFocusEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQFocusEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQFont.cc b/src/gsiqt/qt5/QtGui/gsiDeclQFont.cc index a5dc66e00..d74f6920d 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQFont.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQFont.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQFontDatabase.cc b/src/gsiqt/qt5/QtGui/gsiDeclQFontDatabase.cc index bc095a179..1aadab326 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQFontDatabase.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQFontDatabase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQFontInfo.cc b/src/gsiqt/qt5/QtGui/gsiDeclQFontInfo.cc index 091c3fac3..e82cae40d 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQFontInfo.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQFontInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQFontMetrics.cc b/src/gsiqt/qt5/QtGui/gsiDeclQFontMetrics.cc index a630cf290..b07b9d4e9 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQFontMetrics.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQFontMetrics.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQFontMetricsF.cc b/src/gsiqt/qt5/QtGui/gsiDeclQFontMetricsF.cc index ad80a8d7d..7de840f2c 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQFontMetricsF.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQFontMetricsF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQGenericPlugin.cc b/src/gsiqt/qt5/QtGui/gsiDeclQGenericPlugin.cc index 1ebfac275..52cd439e9 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQGenericPlugin.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQGenericPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQGenericPluginFactory.cc b/src/gsiqt/qt5/QtGui/gsiDeclQGenericPluginFactory.cc index fcc2fd858..28d97ec3c 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQGenericPluginFactory.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQGenericPluginFactory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQGlyphRun.cc b/src/gsiqt/qt5/QtGui/gsiDeclQGlyphRun.cc index 91b72864f..ec6886031 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQGlyphRun.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQGlyphRun.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQGradient.cc b/src/gsiqt/qt5/QtGui/gsiDeclQGradient.cc index a181d8891..a4aa0e77c 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQGradient.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQGradient.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQGuiApplication.cc b/src/gsiqt/qt5/QtGui/gsiDeclQGuiApplication.cc index d01e730f9..597b3eb60 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQGuiApplication.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQGuiApplication.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQHelpEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQHelpEvent.cc index 050fcbc60..d6dd58289 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQHelpEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQHelpEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQHideEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQHideEvent.cc index edee988b3..6c10f5477 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQHideEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQHideEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQHoverEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQHoverEvent.cc index 512b90950..cccbd9993 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQHoverEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQHoverEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQIcon.cc b/src/gsiqt/qt5/QtGui/gsiDeclQIcon.cc index 537134eef..169c3da79 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQIcon.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQIcon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQIconDragEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQIconDragEvent.cc index 19edf4bef..40eed1a26 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQIconDragEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQIconDragEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQIconEngine.cc b/src/gsiqt/qt5/QtGui/gsiDeclQIconEngine.cc index af371ce5c..734ef0c42 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQIconEngine.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQIconEngine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQIconEnginePlugin.cc b/src/gsiqt/qt5/QtGui/gsiDeclQIconEnginePlugin.cc index 1b8f55020..e5e3c51d8 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQIconEnginePlugin.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQIconEnginePlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQIconEngine_AvailableSizesArgument.cc b/src/gsiqt/qt5/QtGui/gsiDeclQIconEngine_AvailableSizesArgument.cc index a39a85428..e651e99a2 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQIconEngine_AvailableSizesArgument.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQIconEngine_AvailableSizesArgument.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQImage.cc b/src/gsiqt/qt5/QtGui/gsiDeclQImage.cc index 186c0eed8..f6c65cb35 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQImage.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQImage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQImageIOHandler.cc b/src/gsiqt/qt5/QtGui/gsiDeclQImageIOHandler.cc index c849d2e3a..04996c2bc 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQImageIOHandler.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQImageIOHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQImageIOPlugin.cc b/src/gsiqt/qt5/QtGui/gsiDeclQImageIOPlugin.cc index fdf1d60e7..6ddf8f5c7 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQImageIOPlugin.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQImageIOPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQImageReader.cc b/src/gsiqt/qt5/QtGui/gsiDeclQImageReader.cc index e32ccd985..f9d4ccf2f 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQImageReader.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQImageReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQImageWriter.cc b/src/gsiqt/qt5/QtGui/gsiDeclQImageWriter.cc index f0837fd55..2a5ee8714 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQImageWriter.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQImageWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQInputEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQInputEvent.cc index 946c88b5f..b8bf78327 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQInputEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQInputEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQInputMethod.cc b/src/gsiqt/qt5/QtGui/gsiDeclQInputMethod.cc index 23a1fd555..f185bf1ff 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQInputMethod.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQInputMethod.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodEvent.cc index 662888c6c..345c466af 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodEvent_Attribute.cc b/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodEvent_Attribute.cc index 406e68321..32609e53d 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodEvent_Attribute.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodEvent_Attribute.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodQueryEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodQueryEvent.cc index 0b049a739..e47a2a2c1 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodQueryEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQInputMethodQueryEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQIntValidator.cc b/src/gsiqt/qt5/QtGui/gsiDeclQIntValidator.cc index 3c02f7312..a894e326b 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQIntValidator.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQIntValidator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQKeyEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQKeyEvent.cc index ec52e9cb6..7196433a4 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQKeyEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQKeyEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQKeySequence.cc b/src/gsiqt/qt5/QtGui/gsiDeclQKeySequence.cc index 6d9f1410f..23ae2491a 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQKeySequence.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQKeySequence.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQLinearGradient.cc b/src/gsiqt/qt5/QtGui/gsiDeclQLinearGradient.cc index f23620fe4..d4ea4aaac 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQLinearGradient.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQLinearGradient.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQMatrix.cc b/src/gsiqt/qt5/QtGui/gsiDeclQMatrix.cc index ebfe7870c..e203a5b03 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQMatrix.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQMatrix.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQMatrix4x4.cc b/src/gsiqt/qt5/QtGui/gsiDeclQMatrix4x4.cc index 434bdabf5..8fab7b0dd 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQMatrix4x4.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQMatrix4x4.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQMouseEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQMouseEvent.cc index 570e94e63..b5bae0713 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQMouseEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQMouseEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQMoveEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQMoveEvent.cc index b90bd6be5..0cbfb09e0 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQMoveEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQMoveEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQMovie.cc b/src/gsiqt/qt5/QtGui/gsiDeclQMovie.cc index 8926b3d6e..3384e6959 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQMovie.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQMovie.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQNativeGestureEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQNativeGestureEvent.cc index 44fc3b88e..542e8ccec 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQNativeGestureEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQNativeGestureEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQOffscreenSurface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQOffscreenSurface.cc index 712fb98a8..68ab4cc86 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQOffscreenSurface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQOffscreenSurface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPageLayout.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPageLayout.cc index b029315f8..14e41a60b 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPageLayout.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPageLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPageSize.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPageSize.cc index 09940346d..a99eaf12f 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPageSize.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPageSize.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPagedPaintDevice.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPagedPaintDevice.cc index af6fd7fa8..4369f8537 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPagedPaintDevice.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPagedPaintDevice.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPagedPaintDevice_Margins.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPagedPaintDevice_Margins.cc index 39bbaa009..f2f1d332a 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPagedPaintDevice_Margins.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPagedPaintDevice_Margins.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPaintDevice.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPaintDevice.cc index 92102f4a3..988c13bfa 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPaintDevice.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPaintDevice.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPaintDeviceWindow.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPaintDeviceWindow.cc index 6c2548e61..0db0f507e 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPaintDeviceWindow.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPaintDeviceWindow.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPaintEngine.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPaintEngine.cc index d2feea193..d0a992e8f 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPaintEngine.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPaintEngine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPaintEngineState.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPaintEngineState.cc index 24aa70e34..d72435c0b 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPaintEngineState.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPaintEngineState.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPaintEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPaintEvent.cc index 4a0e9b500..769133bdb 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPaintEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPaintEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPainter.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPainter.cc index 75d1a094c..2e382495f 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPainter.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPainter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPainterPath.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPainterPath.cc index 5da1c67eb..0defc7280 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPainterPath.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPainterPath.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPainterPathStroker.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPainterPathStroker.cc index f7735c6c0..e826348f2 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPainterPathStroker.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPainterPathStroker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPainterPath_Element.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPainterPath_Element.cc index dfe8b9935..c5770b0ba 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPainterPath_Element.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPainterPath_Element.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPainter_PixmapFragment.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPainter_PixmapFragment.cc index 504beec9c..b9f0381b9 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPainter_PixmapFragment.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPainter_PixmapFragment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPalette.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPalette.cc index 010d9e540..fc12d8ee7 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPalette.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPalette.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPdfWriter.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPdfWriter.cc index dadbef8a6..a2780bdad 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPdfWriter.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPdfWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPen.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPen.cc index 4fe6845a1..999ab9691 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPen.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPen.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPicture.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPicture.cc index c6ce3c86f..12e646b99 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPicture.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPicture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPictureFormatPlugin.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPictureFormatPlugin.cc index ea832d778..1ea3a3b32 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPictureFormatPlugin.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPictureFormatPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPixelFormat.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPixelFormat.cc index e3a24a9c2..38d0a39fe 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPixelFormat.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPixelFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPixmap.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPixmap.cc index 040e6da88..a3cfda596 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPixmap.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPixmap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPixmapCache.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPixmapCache.cc index 85d677fad..625c45db3 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPixmapCache.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPixmapCache.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPlatformSurfaceEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPlatformSurfaceEvent.cc index a90d54c99..a1408fd72 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPlatformSurfaceEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPlatformSurfaceEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPolygon.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPolygon.cc index db71e927c..5938ff8eb 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPolygon.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPolygon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQPolygonF.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPolygonF.cc index 409d8a28e..3a7d31272 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPolygonF.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPolygonF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQQuaternion.cc b/src/gsiqt/qt5/QtGui/gsiDeclQQuaternion.cc index ad6b03a15..2620630dd 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQQuaternion.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQQuaternion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQRadialGradient.cc b/src/gsiqt/qt5/QtGui/gsiDeclQRadialGradient.cc index 0eea05ac7..d79443b31 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQRadialGradient.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQRadialGradient.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQRasterWindow.cc b/src/gsiqt/qt5/QtGui/gsiDeclQRasterWindow.cc index 9e5084d49..a97eaff3a 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQRasterWindow.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQRasterWindow.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQRawFont.cc b/src/gsiqt/qt5/QtGui/gsiDeclQRawFont.cc index 80e7a94bb..1b2524501 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQRawFont.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQRawFont.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQRegExpValidator.cc b/src/gsiqt/qt5/QtGui/gsiDeclQRegExpValidator.cc index 7f645ad20..6c6fa68ec 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQRegExpValidator.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQRegExpValidator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQRegion.cc b/src/gsiqt/qt5/QtGui/gsiDeclQRegion.cc index dbbe7c43e..542a8fbf5 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQRegion.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQRegion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQRegularExpressionValidator.cc b/src/gsiqt/qt5/QtGui/gsiDeclQRegularExpressionValidator.cc index 113e63e45..fc46c6a5f 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQRegularExpressionValidator.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQRegularExpressionValidator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQResizeEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQResizeEvent.cc index 3e8ddeea9..d3d5bd4d8 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQResizeEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQResizeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQScreen.cc b/src/gsiqt/qt5/QtGui/gsiDeclQScreen.cc index c12065c20..c97db766b 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQScreen.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQScreen.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQScreenOrientationChangeEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQScreenOrientationChangeEvent.cc index 6804fc379..bd33fe367 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQScreenOrientationChangeEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQScreenOrientationChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQScrollEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQScrollEvent.cc index 7d4589c01..cb2762db9 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQScrollEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQScrollEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQScrollPrepareEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQScrollPrepareEvent.cc index ece5eba32..d4a4cf303 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQScrollPrepareEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQScrollPrepareEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQSessionManager.cc b/src/gsiqt/qt5/QtGui/gsiDeclQSessionManager.cc index 75f354c7d..9c2aaead4 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQSessionManager.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQSessionManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQShortcutEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQShortcutEvent.cc index 1dda07690..c9c40e279 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQShortcutEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQShortcutEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQShowEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQShowEvent.cc index 87a7ebcab..44e15293a 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQShowEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQShowEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQStandardItem.cc b/src/gsiqt/qt5/QtGui/gsiDeclQStandardItem.cc index 88099d918..c981e2c61 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQStandardItem.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQStandardItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQStandardItemModel.cc b/src/gsiqt/qt5/QtGui/gsiDeclQStandardItemModel.cc index 9c7f118a2..1a1159c21 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQStandardItemModel.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQStandardItemModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQStaticText.cc b/src/gsiqt/qt5/QtGui/gsiDeclQStaticText.cc index 04d02cbab..cd476e7ab 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQStaticText.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQStaticText.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQStatusTipEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQStatusTipEvent.cc index 0fe6aba43..dec989937 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQStatusTipEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQStatusTipEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQStyleHints.cc b/src/gsiqt/qt5/QtGui/gsiDeclQStyleHints.cc index b89130b33..468eaddce 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQStyleHints.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQStyleHints.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQSurface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQSurface.cc index 23435188e..df0dc79ad 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQSurface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQSurface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQSurfaceFormat.cc b/src/gsiqt/qt5/QtGui/gsiDeclQSurfaceFormat.cc index 7642a4711..30f621694 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQSurfaceFormat.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQSurfaceFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQSyntaxHighlighter.cc b/src/gsiqt/qt5/QtGui/gsiDeclQSyntaxHighlighter.cc index 99a10b53e..8dd7fe228 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQSyntaxHighlighter.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQSyntaxHighlighter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTabletEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTabletEvent.cc index 81dbc204f..064b5e351 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTabletEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTabletEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextBlock.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextBlock.cc index 798012ea0..93be38a87 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextBlock.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextBlock.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockFormat.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockFormat.cc index f6f9cbb14..1b7456685 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockFormat.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockGroup.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockGroup.cc index b2ee6517f..fd0113f64 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockGroup.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockUserData.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockUserData.cc index 7e869019e..ef4259ae5 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockUserData.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextBlockUserData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextBlock_Iterator.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextBlock_Iterator.cc index 6659c42a2..84f1eceef 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextBlock_Iterator.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextBlock_Iterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextCharFormat.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextCharFormat.cc index 734005cef..6880a0c79 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextCharFormat.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextCharFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextCursor.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextCursor.cc index 9714f3384..5e10235e3 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextCursor.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextCursor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextDocument.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextDocument.cc index 27fdb3abc..4dc5d40d5 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextDocument.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextDocument.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextDocumentFragment.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextDocumentFragment.cc index 14f5706b2..b67da5880 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextDocumentFragment.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextDocumentFragment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextDocumentWriter.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextDocumentWriter.cc index dda660fc8..5dff3682f 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextDocumentWriter.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextDocumentWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextFormat.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextFormat.cc index cf1e102c9..7ceede5c5 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextFormat.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextFragment.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextFragment.cc index 48655538a..f89030a1f 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextFragment.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextFragment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextFrame.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextFrame.cc index 3dfc93961..f3d70a52e 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextFrame.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextFrameFormat.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextFrameFormat.cc index 778aeeb63..213f46c18 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextFrameFormat.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextFrameFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextFrame_Iterator.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextFrame_Iterator.cc index ece110cbe..c7420b980 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextFrame_Iterator.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextFrame_Iterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextImageFormat.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextImageFormat.cc index 44e42b87d..af11e1bef 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextImageFormat.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextImageFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextInlineObject.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextInlineObject.cc index 1bdf78daa..dfb07a91a 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextInlineObject.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextInlineObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextItem.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextItem.cc index 3c1bb41ff..14521e6ee 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextItem.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextLayout.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextLayout.cc index 532a5aca0..26b1a6e98 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextLayout.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextLayout_FormatRange.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextLayout_FormatRange.cc index 4cbe7c962..e9d84462e 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextLayout_FormatRange.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextLayout_FormatRange.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextLength.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextLength.cc index 9b97d91e3..7bc0e4b80 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextLength.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextLength.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextLine.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextLine.cc index b3e0502b2..461bab607 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextLine.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextLine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextList.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextList.cc index 0872f6743..748c0d686 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextList.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextList.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextListFormat.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextListFormat.cc index 98b677caf..94b1dc6d3 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextListFormat.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextListFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextObject.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextObject.cc index 7295d8d42..a8dbbd164 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextObject.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextObjectInterface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextObjectInterface.cc index 6060a3eb9..6247525c2 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextObjectInterface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextObjectInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextOption.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextOption.cc index dcf93e020..d1ef19dfb 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextOption.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextOption.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextOption_Tab.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextOption_Tab.cc index 506fb9d37..07c14dae5 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextOption_Tab.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextOption_Tab.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextTable.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextTable.cc index 0b41459d1..23d948da7 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextTable.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextTable.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextTableCell.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextTableCell.cc index 2403c7628..28b679e8d 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextTableCell.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextTableCell.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextTableCellFormat.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextTableCellFormat.cc index 54e54493a..f8074f0d3 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextTableCellFormat.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextTableCellFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTextTableFormat.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTextTableFormat.cc index 88aa993be..121026041 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTextTableFormat.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTextTableFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQToolBarChangeEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQToolBarChangeEvent.cc index d98aac978..7861b7872 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQToolBarChangeEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQToolBarChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTouchDevice.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTouchDevice.cc index 7e706f462..f074fdf01 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTouchDevice.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTouchDevice.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTouchEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTouchEvent.cc index 5539f2687..6d328e697 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTouchEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTouchEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTouchEvent_TouchPoint.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTouchEvent_TouchPoint.cc index 4a17f9aad..e15989d5b 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTouchEvent_TouchPoint.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTouchEvent_TouchPoint.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQTransform.cc b/src/gsiqt/qt5/QtGui/gsiDeclQTransform.cc index 4d50db817..00f1ab05c 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQTransform.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQTransform.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQValidator.cc b/src/gsiqt/qt5/QtGui/gsiDeclQValidator.cc index ae9af3c90..f1a662930 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQValidator.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQValidator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQVector2D.cc b/src/gsiqt/qt5/QtGui/gsiDeclQVector2D.cc index 98ec4c596..55adaee1b 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQVector2D.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQVector2D.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQVector3D.cc b/src/gsiqt/qt5/QtGui/gsiDeclQVector3D.cc index 6dc0f80e0..58e2017ab 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQVector3D.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQVector3D.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQVector4D.cc b/src/gsiqt/qt5/QtGui/gsiDeclQVector4D.cc index f9fdc49ac..53bdb8010 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQVector4D.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQVector4D.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQWhatsThisClickedEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQWhatsThisClickedEvent.cc index 44555ef3f..adddd2004 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQWhatsThisClickedEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQWhatsThisClickedEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQWheelEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQWheelEvent.cc index 77d7daee0..5b865f790 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQWheelEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQWheelEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQWindow.cc b/src/gsiqt/qt5/QtGui/gsiDeclQWindow.cc index 57b375867..4946480f6 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQWindow.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQWindow.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQWindowStateChangeEvent.cc b/src/gsiqt/qt5/QtGui/gsiDeclQWindowStateChangeEvent.cc index 773ee9b02..cdf7f4065 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQWindowStateChangeEvent.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQWindowStateChangeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQtGuiAdd.cc b/src/gsiqt/qt5/QtGui/gsiDeclQtGuiAdd.cc index 97eceb3d4..1d7e787b4 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQtGuiAdd.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQtGuiAdd.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQtGuiTypeTraits.h b/src/gsiqt/qt5/QtGui/gsiDeclQtGuiTypeTraits.h index 8f89b886f..8ac2c3fd4 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQtGuiTypeTraits.h +++ b/src/gsiqt/qt5/QtGui/gsiDeclQtGuiTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtGui/gsiQtExternals.h b/src/gsiqt/qt5/QtGui/gsiQtExternals.h index b010fceea..04c4e3733 100644 --- a/src/gsiqt/qt5/QtGui/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtGui/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioDeviceInfo.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioDeviceInfo.cc index 3b950624b..672c84282 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioDeviceInfo.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioDeviceInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioInput.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioInput.cc index 7ff77ce9e..7d0decc06 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioInput.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioInput.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioOutput.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioOutput.cc index 4c51a7d04..23b61a7b6 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioOutput.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractAudioOutput.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractNetworkCache.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractNetworkCache.cc index 2ea404751..91f343ba7 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractNetworkCache.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractNetworkCache.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractSocket.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractSocket.cc index d31a8b4a6..8407068fb 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractSocket.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoBuffer.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoBuffer.cc index 8d7e1263f..1a5e4c980 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoBuffer.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoBuffer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoFilter.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoFilter.cc index 9d346badb..f19151636 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoFilter.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoFilter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoSurface.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoSurface.cc index aa551a3e0..ab4cbbd69 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoSurface.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAbstractVideoSurface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudio.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudio.cc index 463e5e24a..7962a8a03 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudio.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudio.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioBuffer.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioBuffer.cc index 92767f418..70617be3a 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioBuffer.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioBuffer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDecoder.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDecoder.cc index cde18f723..8a68da07f 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDecoder.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDecoder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDecoderControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDecoderControl.cc index f68bd4afd..b2a7ae5cf 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDecoderControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDecoderControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDeviceInfo.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDeviceInfo.cc index cc5e542bb..3c988c4ef 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDeviceInfo.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioDeviceInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioEncoderSettings.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioEncoderSettings.cc index 42b134af2..5a7b9c09b 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioEncoderSettings.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioEncoderSettings.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioEncoderSettingsControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioEncoderSettingsControl.cc index e0613cab5..b2bc7fdc5 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioEncoderSettingsControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioEncoderSettingsControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioFormat.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioFormat.cc index e366f99da..864021cb8 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioFormat.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioInput.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioInput.cc index c726384d9..37bc1c78d 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioInput.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioInput.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioInputSelectorControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioInputSelectorControl.cc index 559891900..1ccc9c7a8 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioInputSelectorControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioInputSelectorControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioOutput.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioOutput.cc index c2e7d359d..5ef6dfedd 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioOutput.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioOutput.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioOutputSelectorControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioOutputSelectorControl.cc index e079e9646..1aa487e13 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioOutputSelectorControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioOutputSelectorControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioProbe.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioProbe.cc index 7af9d82a8..43d06d241 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioProbe.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioProbe.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioRecorder.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioRecorder.cc index 4a7824701..70d780500 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioRecorder.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioRecorder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemFactoryInterface.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemFactoryInterface.cc index 7290928ff..ec0dd89ac 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemFactoryInterface.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemFactoryInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemPlugin.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemPlugin.cc index ecbe71b49..8ac50dfb9 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemPlugin.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAuthenticator.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAuthenticator.cc index ee61c2f3f..40375e1db 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAuthenticator.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAuthenticator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCamera.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCamera.cc index d8a685e0e..ef300ade4 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCamera.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCamera.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraCaptureBufferFormatControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraCaptureBufferFormatControl.cc index 43ea777a4..43cbfab24 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraCaptureBufferFormatControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraCaptureBufferFormatControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraCaptureDestinationControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraCaptureDestinationControl.cc index 3411db93b..f233c005b 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraCaptureDestinationControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraCaptureDestinationControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraControl.cc index 131fd85ff..d78f9a1f5 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraExposure.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraExposure.cc index 26b595d22..9bbd68fc5 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraExposure.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraExposure.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraExposureControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraExposureControl.cc index 319b72b7c..2a3f48e1f 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraExposureControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraExposureControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFeedbackControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFeedbackControl.cc index c3525a6ae..e4dd4a6c9 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFeedbackControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFeedbackControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFlashControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFlashControl.cc index c85832054..0c1190db9 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFlashControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFlashControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocus.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocus.cc index 4c246bfbb..6c64b3c0b 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocus.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocus.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocusControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocusControl.cc index 16914f13a..403ad58ca 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocusControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocusControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocusZone.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocusZone.cc index b089d7fa8..6d671ca77 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocusZone.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraFocusZone.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCapture.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCapture.cc index 30d44fec3..5c64490df 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCapture.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCapture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCaptureControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCaptureControl.cc index eade519b0..ceeb044d6 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCaptureControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCaptureControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageProcessing.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageProcessing.cc index 178f4c54a..a7f83d932 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageProcessing.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageProcessing.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageProcessingControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageProcessingControl.cc index a6a486b98..9a70bdcdf 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageProcessingControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageProcessingControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraInfo.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraInfo.cc index c2490b489..1e576db6f 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraInfo.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraInfoControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraInfoControl.cc index e0e677166..cd240193a 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraInfoControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraInfoControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraLocksControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraLocksControl.cc index 48def1dcf..57679c63d 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraLocksControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraLocksControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettings.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettings.cc index fe2113e18..097dae9cc 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettings.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettings.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettingsControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettingsControl.cc index 0e4edede5..6e729fce6 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettingsControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettingsControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettingsControl2.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettingsControl2.cc index f7f6c2701..4a6836220 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettingsControl2.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraViewfinderSettingsControl2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraZoomControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraZoomControl.cc index 5a21eb03d..c062e24ae 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraZoomControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraZoomControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCamera_FrameRateRange.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCamera_FrameRateRange.cc index 0e3045a4d..28fb65949 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCamera_FrameRateRange.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCamera_FrameRateRange.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsDomainNameRecord.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsDomainNameRecord.cc index d7deb2114..def4ce770 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsDomainNameRecord.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsDomainNameRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsHostAddressRecord.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsHostAddressRecord.cc index 4fac7e30f..48069ff0f 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsHostAddressRecord.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsHostAddressRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsLookup.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsLookup.cc index 811db1473..f4782c0ea 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsLookup.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsLookup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsMailExchangeRecord.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsMailExchangeRecord.cc index 1c5e67359..29ca428ab 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsMailExchangeRecord.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsMailExchangeRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsServiceRecord.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsServiceRecord.cc index b8b6d7eb0..72b5a8621 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsServiceRecord.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsServiceRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsTextRecord.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsTextRecord.cc index a3a175145..5ec79384c 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsTextRecord.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQDnsTextRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQGraphicsVideoItem.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQGraphicsVideoItem.cc index 286642422..adb41e2d8 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQGraphicsVideoItem.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQGraphicsVideoItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQHostAddress.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQHostAddress.cc index 22a74d1ac..e92c38c1e 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQHostAddress.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQHostAddress.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQHostInfo.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQHostInfo.cc index 66d4fbe1b..1baabf9ce 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQHostInfo.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQHostInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQHttpMultiPart.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQHttpMultiPart.cc index cd49fdc53..aad6411d8 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQHttpMultiPart.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQHttpMultiPart.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQHttpPart.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQHttpPart.cc index d63f86975..68207ec82 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQHttpPart.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQHttpPart.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQIPv6Address.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQIPv6Address.cc index ed0814403..89a2118fb 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQIPv6Address.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQIPv6Address.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQImageEncoderControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQImageEncoderControl.cc index d49383701..be9795f8a 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQImageEncoderControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQImageEncoderControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQImageEncoderSettings.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQImageEncoderSettings.cc index 78ebf3739..7a69607e6 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQImageEncoderSettings.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQImageEncoderSettings.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQLocalServer.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQLocalServer.cc index fe49cd33b..437bfb2c0 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQLocalServer.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQLocalServer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQLocalSocket.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQLocalSocket.cc index 86bc5dcd1..d45235b1f 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQLocalSocket.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQLocalSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaAudioProbeControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaAudioProbeControl.cc index b10469772..32481003b 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaAudioProbeControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaAudioProbeControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaAvailabilityControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaAvailabilityControl.cc index 3d484c5bb..a32a9ffeb 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaAvailabilityControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaAvailabilityControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaBindableInterface.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaBindableInterface.cc index 7be21bd08..8f5c722dd 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaBindableInterface.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaBindableInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaContainerControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaContainerControl.cc index 3b27eb415..afefad611 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaContainerControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaContainerControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaContent.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaContent.cc index fbb33b176..cd2d216f1 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaContent.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaContent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaControl.cc index b1970589d..f30df3005 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaGaplessPlaybackControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaGaplessPlaybackControl.cc index 733bcb10b..804861f53 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaGaplessPlaybackControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaGaplessPlaybackControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaMetaData.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaMetaData.cc index 0431458bc..6985c7497 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaMetaData.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaMetaData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaNetworkAccessControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaNetworkAccessControl.cc index ff7aff8b3..4dc093666 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaNetworkAccessControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaNetworkAccessControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaObject.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaObject.cc index 47b63eb4d..f5c52b8ea 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaObject.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlayer.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlayer.cc index aff5a4db2..e44e61d77 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlayer.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlayer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlayerControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlayerControl.cc index dafabb543..0f70642d6 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlayerControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlayerControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlaylist.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlaylist.cc index 66360eb0e..44b421930 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlaylist.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlaylist.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorder.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorder.cc index 52bbf9800..570344144 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorder.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorderControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorderControl.cc index e8b5fc8ca..e5c71af24 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorderControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorderControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaResource.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaResource.cc index 29fe5c810..031dc1f25 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaResource.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaResource.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaService.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaService.cc index d0e9b3ef1..af42cdc71 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaService.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaService.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceCameraInfoInterface.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceCameraInfoInterface.cc index 84450053d..13588eaa5 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceCameraInfoInterface.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceCameraInfoInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceDefaultDeviceInterface.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceDefaultDeviceInterface.cc index c5dbdfb6a..9f43006b0 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceDefaultDeviceInterface.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceDefaultDeviceInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceFeaturesInterface.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceFeaturesInterface.cc index 78791a594..ba20ff5a1 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceFeaturesInterface.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceFeaturesInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderFactoryInterface.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderFactoryInterface.cc index b3dac0f10..5e0c30612 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderFactoryInterface.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderFactoryInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderHint.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderHint.cc index 88f071971..3d3618c16 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderHint.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderHint.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderPlugin.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderPlugin.cc index fedf81bfe..7d743d664 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderPlugin.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceSupportedDevicesInterface.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceSupportedDevicesInterface.cc index d03d76eaa..955b5bf96 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceSupportedDevicesInterface.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceSupportedDevicesInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceSupportedFormatsInterface.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceSupportedFormatsInterface.cc index 932105048..367cad4af 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceSupportedFormatsInterface.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceSupportedFormatsInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaStreamsControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaStreamsControl.cc index 08c852520..409818499 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaStreamsControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaStreamsControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaTimeInterval.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaTimeInterval.cc index 0e902db5c..4bad2659b 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaTimeInterval.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaTimeInterval.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaTimeRange.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaTimeRange.cc index 23721505f..b6739b2d4 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaTimeRange.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaTimeRange.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaVideoProbeControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaVideoProbeControl.cc index 6e166104d..5d6db9232 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaVideoProbeControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaVideoProbeControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMetaDataReaderControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMetaDataReaderControl.cc index bc5f6cbba..aa6144820 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMetaDataReaderControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMetaDataReaderControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMetaDataWriterControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMetaDataWriterControl.cc index ed72d8295..be8a08cdf 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMetaDataWriterControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMetaDataWriterControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMultimedia.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMultimedia.cc index 64cd7e631..6112f2715 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMultimedia.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMultimedia.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkAccessManager.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkAccessManager.cc index fec9ef7d6..e3f36c1e0 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkAccessManager.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkAccessManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkAddressEntry.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkAddressEntry.cc index 306c3ed63..f86eaf27a 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkAddressEntry.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkAddressEntry.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCacheMetaData.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCacheMetaData.cc index d26e6245a..e8164fa0f 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCacheMetaData.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCacheMetaData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkConfiguration.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkConfiguration.cc index 7d1180235..da90a05d2 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkConfiguration.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkConfiguration.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkConfigurationManager.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkConfigurationManager.cc index 6783b5e0f..c56d1ffd6 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkConfigurationManager.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkConfigurationManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCookie.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCookie.cc index e98eacd7d..865e155c5 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCookie.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCookie.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCookieJar.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCookieJar.cc index a0bd055f4..dfb0290ba 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCookieJar.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkCookieJar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkDiskCache.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkDiskCache.cc index fa3b03d73..8b39f42af 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkDiskCache.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkDiskCache.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkInterface.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkInterface.cc index 022591bc3..a7093b7cd 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkInterface.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxy.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxy.cc index 1b2be5ec1..45f4ab587 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxy.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxy.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxyFactory.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxyFactory.cc index 88c25c950..d676e264d 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxyFactory.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxyFactory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxyQuery.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxyQuery.cc index 43392af6c..58c56fd1b 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxyQuery.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkProxyQuery.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkReply.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkReply.cc index 90211611f..4b4d87000 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkReply.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkReply.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkRequest.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkRequest.cc index 572f742f5..572968f1a 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkRequest.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkRequest.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkSession.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkSession.cc index bcc9443e3..2f3fba61b 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkSession.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQNetworkSession.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioData.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioData.cc index 1259e27b5..caf762fe7 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioData.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioDataControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioDataControl.cc index 9e058b52a..3e0c841d7 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioDataControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioDataControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioTuner.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioTuner.cc index 0083a6894..eb766ca3b 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioTuner.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioTuner.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioTunerControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioTunerControl.cc index 2068a1681..3316cc722 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioTunerControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioTunerControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSound.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSound.cc index c87ad880c..75edb9532 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSound.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSound.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSoundEffect.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSoundEffect.cc index 932a9d04b..323e10a50 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSoundEffect.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSoundEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSsl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSsl.cc index 4bbecb523..4541375e3 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSsl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSsl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCertificate.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCertificate.cc index a5316ff52..5fb54e14a 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCertificate.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCertificate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCertificateExtension.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCertificateExtension.cc index 76a027c67..1eb8030b8 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCertificateExtension.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCertificateExtension.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCipher.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCipher.cc index ea7f4bc6e..0b48546f4 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCipher.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslCipher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslConfiguration.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslConfiguration.cc index 23d2d32c9..a858d5659 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslConfiguration.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslConfiguration.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslEllipticCurve.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslEllipticCurve.cc index f86e460e6..e1cdd5d04 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslEllipticCurve.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslEllipticCurve.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslError.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslError.cc index 28653e8fd..0b560e850 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslError.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslError.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslKey.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslKey.cc index 0138a3551..bcd689f21 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslKey.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslKey.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslPreSharedKeyAuthenticator.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslPreSharedKeyAuthenticator.cc index af1bced51..9cf63e226 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslPreSharedKeyAuthenticator.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslPreSharedKeyAuthenticator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslSocket.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslSocket.cc index 3f52214cc..8ea61ee4a 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslSocket.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQSslSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQTcpServer.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQTcpServer.cc index caca76455..98c349ccc 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQTcpServer.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQTcpServer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQTcpSocket.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQTcpSocket.cc index d33bce1d2..4da742873 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQTcpSocket.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQTcpSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQUdpSocket.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQUdpSocket.cc index 964947f0a..d1fc499b6 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQUdpSocket.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQUdpSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoDeviceSelectorControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoDeviceSelectorControl.cc index 06abbb4f7..534e58855 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoDeviceSelectorControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoDeviceSelectorControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoEncoderSettings.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoEncoderSettings.cc index 8676114b4..1b5ca68fc 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoEncoderSettings.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoEncoderSettings.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoEncoderSettingsControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoEncoderSettingsControl.cc index 6cd06fe2e..9831fe416 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoEncoderSettingsControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoEncoderSettingsControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoFilterRunnable.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoFilterRunnable.cc index 407bf47ce..506ed080b 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoFilterRunnable.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoFilterRunnable.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoFrame.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoFrame.cc index 94fd012fa..a004feddc 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoFrame.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoProbe.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoProbe.cc index f84799552..44f72a920 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoProbe.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoProbe.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoRendererControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoRendererControl.cc index 86d4d330e..bb23ab853 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoRendererControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoRendererControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoSurfaceFormat.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoSurfaceFormat.cc index d1361a4f2..fe535bf2a 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoSurfaceFormat.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoSurfaceFormat.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWidget.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWidget.cc index e398c75c0..9e63f4bf1 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWidget.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWindowControl.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWindowControl.cc index c0ce3e98b..3c970faa6 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWindowControl.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWindowControl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQtMultimediaTypeTraits.h b/src/gsiqt/qt5/QtMultimedia/gsiDeclQtMultimediaTypeTraits.h index c244bbfee..c60cc7934 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQtMultimediaTypeTraits.h +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQtMultimediaTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtMultimedia/gsiQtExternals.h b/src/gsiqt/qt5/QtMultimedia/gsiQtExternals.h index 61c2369a7..2a1eb7fbc 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtMultimedia/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQAbstractNetworkCache.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQAbstractNetworkCache.cc index 10fdd7946..2f6df49f4 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQAbstractNetworkCache.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQAbstractNetworkCache.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQAbstractSocket.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQAbstractSocket.cc index b74efc8b9..1410bce9a 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQAbstractSocket.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQAbstractSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQAuthenticator.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQAuthenticator.cc index 1da366789..6a3f37d45 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQAuthenticator.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQAuthenticator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsDomainNameRecord.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsDomainNameRecord.cc index 8da27e589..b8c02f170 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsDomainNameRecord.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsDomainNameRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsHostAddressRecord.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsHostAddressRecord.cc index c96abecff..aa81e3be9 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsHostAddressRecord.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsHostAddressRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsLookup.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsLookup.cc index eae3ee1b2..41ad8aef1 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsLookup.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsLookup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsMailExchangeRecord.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsMailExchangeRecord.cc index a0ce6f676..fa0c07476 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsMailExchangeRecord.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsMailExchangeRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsServiceRecord.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsServiceRecord.cc index 8d54f9285..f67813b2f 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsServiceRecord.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsServiceRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsTextRecord.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsTextRecord.cc index 624819453..f82cb835f 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsTextRecord.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQDnsTextRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQHostAddress.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQHostAddress.cc index 01e967cc1..1dd14d853 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQHostAddress.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQHostAddress.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQHostInfo.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQHostInfo.cc index d3e67186f..ce30c91a7 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQHostInfo.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQHostInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQHttpMultiPart.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQHttpMultiPart.cc index 0240c2988..3d66009d1 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQHttpMultiPart.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQHttpMultiPart.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQHttpPart.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQHttpPart.cc index 853348c1b..1bdcb0621 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQHttpPart.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQHttpPart.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQIPv6Address.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQIPv6Address.cc index 8817d5bdc..ae71e4a56 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQIPv6Address.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQIPv6Address.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQLocalServer.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQLocalServer.cc index 4b2a40fde..f8eb701ef 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQLocalServer.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQLocalServer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQLocalSocket.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQLocalSocket.cc index a6a1c8af5..164e1d299 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQLocalSocket.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQLocalSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkAccessManager.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkAccessManager.cc index 69671c35e..e8f147aed 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkAccessManager.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkAccessManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkAddressEntry.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkAddressEntry.cc index ef8dc4b20..3e4a58b0f 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkAddressEntry.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkAddressEntry.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCacheMetaData.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCacheMetaData.cc index 3e107b58b..768e17a29 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCacheMetaData.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCacheMetaData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkConfiguration.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkConfiguration.cc index 700abcd4f..c11bdd81e 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkConfiguration.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkConfiguration.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkConfigurationManager.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkConfigurationManager.cc index 38b8a9c25..5584dbf54 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkConfigurationManager.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkConfigurationManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCookie.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCookie.cc index ec2dd8256..7c2924f4e 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCookie.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCookie.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCookieJar.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCookieJar.cc index 2ae79390d..6d2e34fe5 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCookieJar.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkCookieJar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkDiskCache.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkDiskCache.cc index 3cb7cf29d..bc227b8a1 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkDiskCache.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkDiskCache.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkInterface.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkInterface.cc index 94234c334..6531ad7b0 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkInterface.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkInterface.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxy.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxy.cc index 155fe161b..1d7b7d748 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxy.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxy.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxyFactory.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxyFactory.cc index edbdfc9c1..f9da09601 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxyFactory.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxyFactory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxyQuery.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxyQuery.cc index 025b0e40d..150848eba 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxyQuery.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkProxyQuery.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkReply.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkReply.cc index 60ae6fe8d..85ae3142d 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkReply.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkReply.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkRequest.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkRequest.cc index d9472fa45..f328a6742 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkRequest.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkRequest.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkSession.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkSession.cc index 8d460eada..f0d69ac99 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkSession.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQNetworkSession.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQSsl.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQSsl.cc index f5b11718a..62ed2a63b 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQSsl.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQSsl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCertificate.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCertificate.cc index 47c6073cf..c27482fdb 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCertificate.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCertificate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCertificateExtension.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCertificateExtension.cc index 388d6a348..c447c9afa 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCertificateExtension.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCertificateExtension.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCipher.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCipher.cc index 8a36182d5..0c7164123 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCipher.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslCipher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslConfiguration.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslConfiguration.cc index fcb167132..a7cd6ef45 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslConfiguration.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslConfiguration.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslEllipticCurve.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslEllipticCurve.cc index f9a6a38e9..e59425848 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslEllipticCurve.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslEllipticCurve.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslError.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslError.cc index 09e119502..6857207b3 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslError.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslError.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslKey.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslKey.cc index a63c89f8c..184c5af26 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslKey.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslKey.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslPreSharedKeyAuthenticator.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslPreSharedKeyAuthenticator.cc index 15d5ccebf..edf390684 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslPreSharedKeyAuthenticator.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslPreSharedKeyAuthenticator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslSocket.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslSocket.cc index c3105095f..2c44280a0 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQSslSocket.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQSslSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQTcpServer.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQTcpServer.cc index 3680b7ead..ddf33ee2a 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQTcpServer.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQTcpServer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQTcpSocket.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQTcpSocket.cc index 900708beb..d0d2f4659 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQTcpSocket.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQTcpSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQUdpSocket.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQUdpSocket.cc index dcaa659a6..d7257f09c 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQUdpSocket.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQUdpSocket.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQtNetworkAdd.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQtNetworkAdd.cc index 2ece2a754..aa55f9dd7 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQtNetworkAdd.cc +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQtNetworkAdd.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQtNetworkTypeTraits.h b/src/gsiqt/qt5/QtNetwork/gsiDeclQtNetworkTypeTraits.h index 9d57a9ea4..5597a0e02 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiDeclQtNetworkTypeTraits.h +++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQtNetworkTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtNetwork/gsiQtExternals.h b/src/gsiqt/qt5/QtNetwork/gsiQtExternals.h index 8c4e53eca..fd6672e39 100644 --- a/src/gsiqt/qt5/QtNetwork/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtNetwork/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQAbstractPrintDialog.cc b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQAbstractPrintDialog.cc index 2c7515159..0806ed01a 100644 --- a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQAbstractPrintDialog.cc +++ b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQAbstractPrintDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPageSetupDialog.cc b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPageSetupDialog.cc index 671e8d445..68dc91662 100644 --- a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPageSetupDialog.cc +++ b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPageSetupDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintDialog.cc b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintDialog.cc index f19a1a66a..7ac6ab870 100644 --- a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintDialog.cc +++ b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintEngine.cc b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintEngine.cc index 5e70c5b7f..f4942cec3 100644 --- a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintEngine.cc +++ b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintEngine.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintPreviewDialog.cc b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintPreviewDialog.cc index f4508ee17..743ebbd41 100644 --- a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintPreviewDialog.cc +++ b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintPreviewDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintPreviewWidget.cc b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintPreviewWidget.cc index 02ff4ff59..6922e7aa0 100644 --- a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintPreviewWidget.cc +++ b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrintPreviewWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrinter.cc b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrinter.cc index 41d309b5c..f68ff9cdc 100644 --- a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrinter.cc +++ b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrinter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrinterInfo.cc b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrinterInfo.cc index 3a9e4aa82..15b4eb495 100644 --- a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrinterInfo.cc +++ b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQPrinterInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQtPrintSupportTypeTraits.h b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQtPrintSupportTypeTraits.h index e8dfd76ed..eae72f2b3 100644 --- a/src/gsiqt/qt5/QtPrintSupport/gsiDeclQtPrintSupportTypeTraits.h +++ b/src/gsiqt/qt5/QtPrintSupport/gsiDeclQtPrintSupportTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtPrintSupport/gsiQtExternals.h b/src/gsiqt/qt5/QtPrintSupport/gsiQtExternals.h index dbcee1274..846ca976c 100644 --- a/src/gsiqt/qt5/QtPrintSupport/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtPrintSupport/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSql.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSql.cc index 45884dcba..fc131071b 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSql.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSql.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlDatabase.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlDatabase.cc index e36501a60..8c02032c0 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlDatabase.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlDatabase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlDriver.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlDriver.cc index 556dfe01c..f7d87d1e5 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlDriver.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlDriver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlDriverCreatorBase.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlDriverCreatorBase.cc index eb9c51899..e23ed09e3 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlDriverCreatorBase.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlDriverCreatorBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlError.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlError.cc index 085abc8c6..3821d759e 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlError.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlError.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlField.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlField.cc index 067ef8af7..4608cef87 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlField.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlField.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlIndex.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlIndex.cc index f0c81eab8..79b7615c0 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlIndex.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlIndex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlQuery.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlQuery.cc index e03ca5388..0fb502bf4 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlQuery.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlQuery.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlQueryModel.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlQueryModel.cc index 64b6a37b3..67f31ab74 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlQueryModel.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlQueryModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlRecord.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlRecord.cc index c991ccd27..07bf83752 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlRecord.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlRecord.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlRelation.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlRelation.cc index 0051532e2..af5e99d73 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlRelation.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlRelation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlRelationalTableModel.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlRelationalTableModel.cc index bbcfa346b..cad172dfe 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlRelationalTableModel.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlRelationalTableModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlResult.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlResult.cc index 9d7fcbaee..1b944f579 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlResult.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlResult.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQSqlTableModel.cc b/src/gsiqt/qt5/QtSql/gsiDeclQSqlTableModel.cc index e7e3db430..69569d0a1 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQSqlTableModel.cc +++ b/src/gsiqt/qt5/QtSql/gsiDeclQSqlTableModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiDeclQtSqlTypeTraits.h b/src/gsiqt/qt5/QtSql/gsiDeclQtSqlTypeTraits.h index f8494388a..185f4aca4 100644 --- a/src/gsiqt/qt5/QtSql/gsiDeclQtSqlTypeTraits.h +++ b/src/gsiqt/qt5/QtSql/gsiDeclQtSqlTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSql/gsiQtExternals.h b/src/gsiqt/qt5/QtSql/gsiQtExternals.h index 68b366119..f16a80fda 100644 --- a/src/gsiqt/qt5/QtSql/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtSql/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSvg/gsiDeclQGraphicsSvgItem.cc b/src/gsiqt/qt5/QtSvg/gsiDeclQGraphicsSvgItem.cc index 4e36d02db..6c2ac6a64 100644 --- a/src/gsiqt/qt5/QtSvg/gsiDeclQGraphicsSvgItem.cc +++ b/src/gsiqt/qt5/QtSvg/gsiDeclQGraphicsSvgItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSvg/gsiDeclQSvgGenerator.cc b/src/gsiqt/qt5/QtSvg/gsiDeclQSvgGenerator.cc index d676ef637..ebcd3578b 100644 --- a/src/gsiqt/qt5/QtSvg/gsiDeclQSvgGenerator.cc +++ b/src/gsiqt/qt5/QtSvg/gsiDeclQSvgGenerator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSvg/gsiDeclQSvgRenderer.cc b/src/gsiqt/qt5/QtSvg/gsiDeclQSvgRenderer.cc index 49863fff0..9d4018430 100644 --- a/src/gsiqt/qt5/QtSvg/gsiDeclQSvgRenderer.cc +++ b/src/gsiqt/qt5/QtSvg/gsiDeclQSvgRenderer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSvg/gsiDeclQSvgWidget.cc b/src/gsiqt/qt5/QtSvg/gsiDeclQSvgWidget.cc index 49f62ed5c..1ef88c5b1 100644 --- a/src/gsiqt/qt5/QtSvg/gsiDeclQSvgWidget.cc +++ b/src/gsiqt/qt5/QtSvg/gsiDeclQSvgWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSvg/gsiDeclQtSvgTypeTraits.h b/src/gsiqt/qt5/QtSvg/gsiDeclQtSvgTypeTraits.h index 0e1b91dab..7493a976c 100644 --- a/src/gsiqt/qt5/QtSvg/gsiDeclQtSvgTypeTraits.h +++ b/src/gsiqt/qt5/QtSvg/gsiDeclQtSvgTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtSvg/gsiQtExternals.h b/src/gsiqt/qt5/QtSvg/gsiQtExternals.h index dad18b0cc..b47be73ca 100644 --- a/src/gsiqt/qt5/QtSvg/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtSvg/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractButton.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractButton.cc index c3680e06c..85b9d4850 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractButton.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractGraphicsShapeItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractGraphicsShapeItem.cc index 2a9f9e634..5a678d063 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractGraphicsShapeItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractGraphicsShapeItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractItemDelegate.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractItemDelegate.cc index 82387b38c..17acfd857 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractItemDelegate.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractItemDelegate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractItemView.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractItemView.cc index 1059c856c..fcd82628a 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractItemView.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractItemView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractScrollArea.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractScrollArea.cc index e7456f273..a4113a1e6 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractScrollArea.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractScrollArea.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractSlider.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractSlider.cc index fa6b4062c..9b0548a01 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractSlider.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractSlider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractSpinBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractSpinBox.cc index 4aad1981f..7adfb7fd0 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractSpinBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQAbstractSpinBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQAccessibleWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQAccessibleWidget.cc index bcf722489..a247d74b4 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQAccessibleWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQAccessibleWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQAction.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQAction.cc index 08807f8cc..541cd55f5 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQAction.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQAction.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQActionGroup.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQActionGroup.cc index 5c7f23adf..3b74479da 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQActionGroup.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQActionGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQApplication.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQApplication.cc index 8ee74c7ae..21cce757a 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQApplication.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQApplication.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQBoxLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQBoxLayout.cc index 61a89bcf6..6744498b3 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQBoxLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQBoxLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQButtonGroup.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQButtonGroup.cc index f88ab7494..2939b0f69 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQButtonGroup.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQButtonGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQCalendarWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQCalendarWidget.cc index f950f1d01..05eff2a30 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQCalendarWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQCalendarWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQCheckBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQCheckBox.cc index f8e5408d6..d67faacab 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQCheckBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQCheckBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQColorDialog.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQColorDialog.cc index 2357881ca..125c73a6b 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQColorDialog.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQColorDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQColormap.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQColormap.cc index 1244a51fc..6c34b8091 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQColormap.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQColormap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQColumnView.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQColumnView.cc index c73983767..0d2143578 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQColumnView.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQColumnView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQComboBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQComboBox.cc index a5bff6ed3..b4ad77981 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQComboBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQComboBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQCommandLinkButton.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQCommandLinkButton.cc index 1e095bbe5..07ff4a903 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQCommandLinkButton.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQCommandLinkButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQCommonStyle.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQCommonStyle.cc index dc30f6189..543e9b76e 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQCommonStyle.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQCommonStyle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQCompleter.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQCompleter.cc index a46e5ccb1..6fd543eba 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQCompleter.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQCompleter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQDataWidgetMapper.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQDataWidgetMapper.cc index 337c1f18b..767317872 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQDataWidgetMapper.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQDataWidgetMapper.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQDateEdit.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQDateEdit.cc index 386af8296..d6bfe1107 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQDateEdit.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQDateEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQDateTimeEdit.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQDateTimeEdit.cc index 58404e107..cf9721b48 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQDateTimeEdit.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQDateTimeEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQDesktopWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQDesktopWidget.cc index 6cc8770ef..1bb60200e 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQDesktopWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQDesktopWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQDial.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQDial.cc index d064c6e7d..9369c2112 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQDial.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQDial.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQDialog.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQDialog.cc index 569ca1eae..657855492 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQDialog.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQDialogButtonBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQDialogButtonBox.cc index 3901d40ab..13487682f 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQDialogButtonBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQDialogButtonBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQDirModel.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQDirModel.cc index c206fcaad..c59bcd9e5 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQDirModel.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQDirModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQDockWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQDockWidget.cc index a3b2e86f3..c43f449ff 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQDockWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQDockWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQDoubleSpinBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQDoubleSpinBox.cc index 6650742e7..344e8869c 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQDoubleSpinBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQDoubleSpinBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQErrorMessage.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQErrorMessage.cc index 99840ddde..6eb0e6de3 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQErrorMessage.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQErrorMessage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQFileDialog.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQFileDialog.cc index d4cf740c0..480161464 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQFileDialog.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQFileDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQFileIconProvider.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQFileIconProvider.cc index f7597ab09..104474710 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQFileIconProvider.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQFileIconProvider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQFileSystemModel.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQFileSystemModel.cc index aac15e9e8..644592a3a 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQFileSystemModel.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQFileSystemModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQFocusFrame.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQFocusFrame.cc index a66a7acea..213310c96 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQFocusFrame.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQFocusFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQFontComboBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQFontComboBox.cc index 55406a969..348cbe5b5 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQFontComboBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQFontComboBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQFontDialog.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQFontDialog.cc index 5f8f82167..b17726798 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQFontDialog.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQFontDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQFormLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQFormLayout.cc index 042224af7..06ca32749 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQFormLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQFormLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQFrame.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQFrame.cc index af2bf3d4a..b2b70ae0c 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQFrame.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGesture.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGesture.cc index 11fdbc0ab..5aca23ce2 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGesture.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGestureEvent.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGestureEvent.cc index 17bc45ba8..01b886dbd 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGestureEvent.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGestureEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGestureRecognizer.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGestureRecognizer.cc index 49dbd870e..3fbbac0a5 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGestureRecognizer.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGestureRecognizer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsAnchor.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsAnchor.cc index c4e98f1d3..a592f2fd3 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsAnchor.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsAnchor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsAnchorLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsAnchorLayout.cc index d80871a1f..5d913e628 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsAnchorLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsAnchorLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsBlurEffect.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsBlurEffect.cc index 6ff361e25..c12c54d70 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsBlurEffect.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsBlurEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsColorizeEffect.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsColorizeEffect.cc index 6ff20eb8d..d8044f610 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsColorizeEffect.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsColorizeEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsDropShadowEffect.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsDropShadowEffect.cc index 4222b292c..5f4027dd8 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsDropShadowEffect.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsDropShadowEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsEffect.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsEffect.cc index 8d5b56a52..c40a5d58a 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsEffect.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsEllipseItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsEllipseItem.cc index d19864849..359a39881 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsEllipseItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsEllipseItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsGridLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsGridLayout.cc index d19ba7d89..d8f9e26b1 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsGridLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsGridLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItem.cc index d841e6c31..ae06d9303 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItemAnimation.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItemAnimation.cc index fc560d325..7d4503257 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItemAnimation.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItemAnimation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItemGroup.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItemGroup.cc index ee8d5d880..abe950d8e 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItemGroup.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsItemGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLayout.cc index 850279fa0..42ed4c34e 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLayoutItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLayoutItem.cc index 4c86fb1eb..e72394333 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLayoutItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLayoutItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLineItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLineItem.cc index be66c9d79..714f91096 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLineItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLineItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLinearLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLinearLayout.cc index 37d2ef60e..33bbbc8d2 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLinearLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsLinearLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsObject.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsObject.cc index 0e1e13183..71392b8d5 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsObject.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsOpacityEffect.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsOpacityEffect.cc index 58ee2786a..5eb71a611 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsOpacityEffect.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsOpacityEffect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPathItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPathItem.cc index bc09bca6c..965bb0386 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPathItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPathItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPixmapItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPixmapItem.cc index 49e4797fa..ae26c9330 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPixmapItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPixmapItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPolygonItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPolygonItem.cc index 46745cc4f..c10e3ffdb 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPolygonItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsPolygonItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsProxyWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsProxyWidget.cc index db0e1d381..2afe442a1 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsProxyWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsProxyWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsRectItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsRectItem.cc index b27aeb9b9..269deb58f 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsRectItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsRectItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsRotation.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsRotation.cc index 1350a82ed..1c7cd43be 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsRotation.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsRotation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsScale.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsScale.cc index 227a4bf92..2120979eb 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsScale.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsScale.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsScene.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsScene.cc index ee407d1b6..b0437144f 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsScene.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsScene.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneContextMenuEvent.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneContextMenuEvent.cc index 77dfffb49..40f5a1f28 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneContextMenuEvent.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneContextMenuEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneDragDropEvent.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneDragDropEvent.cc index bcbfc7bc1..6543ef318 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneDragDropEvent.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneDragDropEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneEvent.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneEvent.cc index 813fe5858..51b0b3723 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneEvent.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneHelpEvent.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneHelpEvent.cc index bb497c56f..fe5ec3a6e 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneHelpEvent.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneHelpEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneHoverEvent.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneHoverEvent.cc index 3faf08e6c..02839f0b7 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneHoverEvent.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneHoverEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneMouseEvent.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneMouseEvent.cc index 43ee0d1fc..cf6ab29c7 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneMouseEvent.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneMouseEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneMoveEvent.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneMoveEvent.cc index 69dafdc43..f81157335 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneMoveEvent.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneMoveEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneResizeEvent.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneResizeEvent.cc index a9ddba458..3e06b67ac 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneResizeEvent.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneResizeEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneWheelEvent.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneWheelEvent.cc index 3f3934da4..901e6b9af 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneWheelEvent.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSceneWheelEvent.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSimpleTextItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSimpleTextItem.cc index 167baaab8..871dc6425 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSimpleTextItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsSimpleTextItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsTextItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsTextItem.cc index 9dd58b974..63915c286 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsTextItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsTextItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsTransform.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsTransform.cc index a236045ae..974a6b481 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsTransform.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsTransform.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsView.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsView.cc index 275e33859..b296843e7 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsView.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsWidget.cc index 125436725..1b01ce0c3 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGridLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGridLayout.cc index 69644cc98..931f9cfda 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGridLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGridLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQGroupBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGroupBox.cc index 30fef02d1..e45a3330f 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGroupBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGroupBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQHBoxLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQHBoxLayout.cc index 9b75842e7..a5da37a65 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQHBoxLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQHBoxLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQHeaderView.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQHeaderView.cc index 4828f1efa..55e86408b 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQHeaderView.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQHeaderView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQInputDialog.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQInputDialog.cc index 10a69cad1..af75f98db 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQInputDialog.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQInputDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQItemDelegate.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQItemDelegate.cc index 6ef48ee19..c10c73883 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQItemDelegate.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQItemDelegate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQItemEditorCreatorBase.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQItemEditorCreatorBase.cc index f3d368c18..fcf64c5d1 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQItemEditorCreatorBase.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQItemEditorCreatorBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQItemEditorFactory.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQItemEditorFactory.cc index caa8b7c3a..145268e6e 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQItemEditorFactory.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQItemEditorFactory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQKeySequenceEdit.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQKeySequenceEdit.cc index 9bacdf160..1c21f2b1c 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQKeySequenceEdit.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQKeySequenceEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQLCDNumber.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQLCDNumber.cc index ed57f33ee..92fb5d325 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQLCDNumber.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQLCDNumber.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQLabel.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQLabel.cc index c1e3d0638..ec54909f4 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQLabel.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQLabel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQLayout.cc index ca8e96e28..bd95d66b0 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQLayoutItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQLayoutItem.cc index cb5cc0a2b..6903e64ba 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQLayoutItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQLayoutItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQLineEdit.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQLineEdit.cc index 669166dfb..cee6bb59a 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQLineEdit.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQLineEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQListView.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQListView.cc index a8e9019a8..1d503505b 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQListView.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQListView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQListWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQListWidget.cc index c1951878e..072b32e2c 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQListWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQListWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQListWidgetItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQListWidgetItem.cc index f0d84d5d4..50ccbe489 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQListWidgetItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQListWidgetItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQMainWindow.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQMainWindow.cc index 7bbe29958..cf809f4f1 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQMainWindow.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQMainWindow.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQMdiArea.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQMdiArea.cc index 0805cd65d..8508dba46 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQMdiArea.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQMdiArea.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQMdiSubWindow.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQMdiSubWindow.cc index 6dafefc4c..bc3ab92bd 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQMdiSubWindow.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQMdiSubWindow.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQMenu.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQMenu.cc index e86acdefa..ff7dcb842 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQMenu.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQMenu.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQMenuBar.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQMenuBar.cc index 247336c8f..ad67813f8 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQMenuBar.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQMenuBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQMessageBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQMessageBox.cc index d5bd9a1b7..9ab4c5439 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQMessageBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQMessageBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQPanGesture.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQPanGesture.cc index b5bb2b44b..80d726df5 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQPanGesture.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQPanGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQPinchGesture.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQPinchGesture.cc index 740f5f4b1..491b74597 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQPinchGesture.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQPinchGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQPlainTextDocumentLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQPlainTextDocumentLayout.cc index 73bea1a13..6488eeb8b 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQPlainTextDocumentLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQPlainTextDocumentLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQPlainTextEdit.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQPlainTextEdit.cc index 1f174049f..527d9a3d6 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQPlainTextEdit.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQPlainTextEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQProgressBar.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQProgressBar.cc index 94731cc8d..b290c8244 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQProgressBar.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQProgressBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQProgressDialog.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQProgressDialog.cc index 41630d4fa..6b3308a11 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQProgressDialog.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQProgressDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQPushButton.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQPushButton.cc index a19809eed..9c25ff83f 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQPushButton.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQPushButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQRadioButton.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQRadioButton.cc index f3c02c72f..e48b5b3ed 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQRadioButton.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQRadioButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQRubberBand.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQRubberBand.cc index 3014295d7..1d05abe66 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQRubberBand.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQRubberBand.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollArea.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollArea.cc index 4d5f86ae3..5c3495b75 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollArea.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollArea.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollBar.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollBar.cc index 734701ad2..a6c0d70d5 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollBar.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQScroller.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQScroller.cc index 6865f3547..ab44d71d3 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQScroller.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQScroller.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollerProperties.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollerProperties.cc index 0570f367a..a98d78e52 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollerProperties.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQScrollerProperties.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQShortcut.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQShortcut.cc index d46589de6..97ddebdfb 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQShortcut.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQShortcut.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQSizeGrip.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQSizeGrip.cc index 1db9aa79b..b135182a9 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQSizeGrip.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQSizeGrip.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQSizePolicy.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQSizePolicy.cc index 5d250b4c6..78b965e5d 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQSizePolicy.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQSizePolicy.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQSlider.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQSlider.cc index e3e048454..66f6032c8 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQSlider.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQSlider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQSpacerItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQSpacerItem.cc index 56be6604c..4d04eb6b7 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQSpacerItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQSpacerItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQSpinBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQSpinBox.cc index c258a8029..909e3b7f3 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQSpinBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQSpinBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQSplashScreen.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQSplashScreen.cc index ecfc0984d..25e12f2ed 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQSplashScreen.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQSplashScreen.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQSplitter.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQSplitter.cc index 09d71197c..afed4fc3c 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQSplitter.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQSplitter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQSplitterHandle.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQSplitterHandle.cc index c3f382570..d76da947c 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQSplitterHandle.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQSplitterHandle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStackedLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStackedLayout.cc index fe074f24a..f49d3c0a8 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStackedLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStackedLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStackedWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStackedWidget.cc index ac3c0f1b5..037b43fba 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStackedWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStackedWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStatusBar.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStatusBar.cc index 80ade0b89..8fb8496bf 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStatusBar.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStatusBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyle.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyle.cc index ba8f93623..ecd9caaa8 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyle.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleFactory.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleFactory.cc index 2bf2a9008..7faa9afe9 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleFactory.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleFactory.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturn.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturn.cc index d269409ab..252fbd477 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturn.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturn.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturnMask.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturnMask.cc index 56d2b05ab..711a9d277 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturnMask.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturnMask.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturnVariant.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturnVariant.cc index 1cb825839..f36f44e1b 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturnVariant.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleHintReturnVariant.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOption.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOption.cc index c82049f18..43747c4fc 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOption.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOption.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionButton.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionButton.cc index b351db9ce..8f90fb59d 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionButton.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionComboBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionComboBox.cc index bc2b08c4a..fa009390d 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionComboBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionComboBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionComplex.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionComplex.cc index eee3c47bb..c974c290f 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionComplex.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionComplex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionDockWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionDockWidget.cc index fb6cc61cb..c719eac88 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionDockWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionDockWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionFocusRect.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionFocusRect.cc index b8a2e7ea7..d5b786d60 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionFocusRect.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionFocusRect.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionFrame.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionFrame.cc index 9c9c1ed2d..e4547216e 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionFrame.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionGraphicsItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionGraphicsItem.cc index c3d77f6eb..a6435f6ed 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionGraphicsItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionGraphicsItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionGroupBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionGroupBox.cc index e831e3c14..f99efec56 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionGroupBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionGroupBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionHeader.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionHeader.cc index edcf10742..706adff8a 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionHeader.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionHeader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionMenuItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionMenuItem.cc index c83f35e8c..00be24f40 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionMenuItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionMenuItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionProgressBar.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionProgressBar.cc index 350a1b2d3..bfc4ebaa0 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionProgressBar.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionProgressBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionRubberBand.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionRubberBand.cc index 27fb1e519..67163162b 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionRubberBand.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionRubberBand.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSizeGrip.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSizeGrip.cc index 1334fa3e6..acc773714 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSizeGrip.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSizeGrip.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSlider.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSlider.cc index 5ca045c9d..6af6dfbdd 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSlider.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSlider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSpinBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSpinBox.cc index c3ba9faa0..dc9e9aa23 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSpinBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionSpinBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTab.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTab.cc index 1486c1ec6..ffe1ce543 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTab.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTab.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTabBarBase.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTabBarBase.cc index d9aebe4d9..287d45a4a 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTabBarBase.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTabBarBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTabWidgetFrame.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTabWidgetFrame.cc index 34e5765f6..ee146c56b 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTabWidgetFrame.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTabWidgetFrame.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTitleBar.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTitleBar.cc index 8c754cb7b..bb8812fb4 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTitleBar.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionTitleBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolBar.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolBar.cc index 435acee91..0c2023f2b 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolBar.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolBox.cc index ab7d83992..fc706dd3f 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolButton.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolButton.cc index 0da88436f..8645c69b8 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolButton.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionToolButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionViewItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionViewItem.cc index 60eb4851b..e4abc927e 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionViewItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyleOptionViewItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStylePainter.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStylePainter.cc index d3e4ce69b..fe40c1fdb 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStylePainter.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStylePainter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStylePlugin.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStylePlugin.cc index 78a2ea097..9089e1fa6 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStylePlugin.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStylePlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyledItemDelegate.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyledItemDelegate.cc index 1ee2c9d8e..1de3aa5f7 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQStyledItemDelegate.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQStyledItemDelegate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQSwipeGesture.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQSwipeGesture.cc index cb0e513f8..8102dde1a 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQSwipeGesture.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQSwipeGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQSystemTrayIcon.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQSystemTrayIcon.cc index f6ae850db..9767ee530 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQSystemTrayIcon.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQSystemTrayIcon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTabBar.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTabBar.cc index a4d139ca2..05cd1d580 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTabBar.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTabBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTabWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTabWidget.cc index a23b7b746..cce4c2342 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTabWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTabWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTableView.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTableView.cc index c9536c1f6..de06c7945 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTableView.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTableView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidget.cc index c91a828eb..4ad3b07c3 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidgetItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidgetItem.cc index d0a1a3d3f..77f573376 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidgetItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidgetItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidgetSelectionRange.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidgetSelectionRange.cc index 82f180cc8..05bdb1245 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidgetSelectionRange.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTableWidgetSelectionRange.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTapAndHoldGesture.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTapAndHoldGesture.cc index ad7f07063..777a1682e 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTapAndHoldGesture.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTapAndHoldGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTapGesture.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTapGesture.cc index b80fa1720..87a1e8619 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTapGesture.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTapGesture.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTextBrowser.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTextBrowser.cc index 248bec52c..e36567ed1 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTextBrowser.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTextBrowser.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTextEdit.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTextEdit.cc index 3a044cfa4..0484f8d0c 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTextEdit.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTextEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTextEdit_ExtraSelection.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTextEdit_ExtraSelection.cc index 3d3fe9f31..196cf388f 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTextEdit_ExtraSelection.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTextEdit_ExtraSelection.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTimeEdit.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTimeEdit.cc index 21f703869..b0465d4a2 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTimeEdit.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTimeEdit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQToolBar.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQToolBar.cc index 0c546f163..37c0b5661 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQToolBar.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQToolBar.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQToolBox.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQToolBox.cc index 448dc5f49..981181cbb 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQToolBox.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQToolBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQToolButton.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQToolButton.cc index 8ba798431..7cb805295 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQToolButton.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQToolButton.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQToolTip.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQToolTip.cc index ca16d7ba4..fc2d31aad 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQToolTip.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQToolTip.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeView.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeView.cc index cfb980f2b..f8e64a8d7 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeView.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidget.cc index f9ad74b06..8deb0210d 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidgetItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidgetItem.cc index 0297c82b2..a31d3ece3 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidgetItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidgetItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidgetItemIterator.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidgetItemIterator.cc index 90c228a27..825ce9980 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidgetItemIterator.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQTreeWidgetItemIterator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoCommand.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoCommand.cc index b09a6dc0a..a08d8dbda 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoCommand.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoCommand.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoGroup.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoGroup.cc index 8d920a63e..4ccbe2db0 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoGroup.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoGroup.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoStack.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoStack.cc index 36668d669..a7dd7f0a6 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoStack.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoStack.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoView.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoView.cc index 5f42d3b5a..56c442dfe 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoView.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQUndoView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQVBoxLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQVBoxLayout.cc index 79613b29d..174a494e1 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQVBoxLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQVBoxLayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQWhatsThis.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQWhatsThis.cc index 6f70236a0..a6d022a29 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQWhatsThis.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQWhatsThis.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQWidget.cc index c2feee134..c3609d868 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQWidgetAction.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQWidgetAction.cc index 267ed70bd..19af99284 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQWidgetAction.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQWidgetAction.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQWidgetItem.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQWidgetItem.cc index 073a75cb5..2af16c012 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQWidgetItem.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQWidgetItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQWizard.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQWizard.cc index d48e1f4b5..83597ba41 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQWizard.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQWizard.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQWizardPage.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQWizardPage.cc index 67c4128b2..84a6c72e7 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQWizardPage.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQWizardPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQtWidgetsAdd.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQtWidgetsAdd.cc index 39b6ec5ce..74f3edb85 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQtWidgetsAdd.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQtWidgetsAdd.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQtWidgetsTypeTraits.h b/src/gsiqt/qt5/QtWidgets/gsiDeclQtWidgetsTypeTraits.h index a13ffab9a..b3b4c4763 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQtWidgetsTypeTraits.h +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQtWidgetsTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtWidgets/gsiQtExternals.h b/src/gsiqt/qt5/QtWidgets/gsiQtExternals.h index 62e8f7a50..f4ed10a79 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtWidgets/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomAttr.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomAttr.cc index c10e55d17..42833bc24 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomAttr.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomAttr.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomCDATASection.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomCDATASection.cc index 28c0ed366..f37e2ed02 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomCDATASection.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomCDATASection.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomCharacterData.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomCharacterData.cc index 2e60ec146..ea6d298f3 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomCharacterData.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomCharacterData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomComment.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomComment.cc index b78d94aa0..175c92d5c 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomComment.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomComment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomDocument.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomDocument.cc index 6db140935..7993dc92d 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomDocument.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomDocument.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomDocumentFragment.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomDocumentFragment.cc index 09043e0a9..0ade67df3 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomDocumentFragment.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomDocumentFragment.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomDocumentType.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomDocumentType.cc index 68d4967c4..c52458e9b 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomDocumentType.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomDocumentType.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomElement.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomElement.cc index 825eb0c95..b7cd58d76 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomElement.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomElement.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomEntity.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomEntity.cc index e1fbbb9cf..fb2c8a672 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomEntity.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomEntity.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomEntityReference.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomEntityReference.cc index 2659cfbcf..901420800 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomEntityReference.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomEntityReference.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomImplementation.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomImplementation.cc index 53ac02abb..34a8a4b5c 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomImplementation.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomImplementation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomNamedNodeMap.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomNamedNodeMap.cc index 2a7b99b30..ab71d9a27 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomNamedNodeMap.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomNamedNodeMap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomNode.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomNode.cc index 438ca14db..910c6a3e2 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomNode.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomNode.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomNodeList.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomNodeList.cc index 6c087475a..086ab6613 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomNodeList.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomNodeList.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomNotation.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomNotation.cc index e5350d0f5..deb27d9ad 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomNotation.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomNotation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomProcessingInstruction.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomProcessingInstruction.cc index 342a4e7cc..e87f787c0 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomProcessingInstruction.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomProcessingInstruction.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQDomText.cc b/src/gsiqt/qt5/QtXml/gsiDeclQDomText.cc index 0cc0bfa51..4dfad040e 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQDomText.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQDomText.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlAttributes.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlAttributes.cc index e583beee4..d5cec2f2c 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlAttributes.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlAttributes.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlContentHandler.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlContentHandler.cc index 1322fe3d6..ba970f86d 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlContentHandler.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlContentHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlDTDHandler.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlDTDHandler.cc index 4281bafc1..1f7c04cd4 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlDTDHandler.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlDTDHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlDeclHandler.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlDeclHandler.cc index 8d65bfa7f..ddc20798c 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlDeclHandler.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlDeclHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlDefaultHandler.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlDefaultHandler.cc index 8f0f6158b..525f02749 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlDefaultHandler.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlDefaultHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlEntityResolver.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlEntityResolver.cc index 83462f189..d3b164445 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlEntityResolver.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlEntityResolver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlErrorHandler.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlErrorHandler.cc index a60b5fa1f..9a8746b16 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlErrorHandler.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlErrorHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlInputSource.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlInputSource.cc index 2a360d264..1a7f90a7d 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlInputSource.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlInputSource.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlLexicalHandler.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlLexicalHandler.cc index da9112317..b3741ac51 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlLexicalHandler.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlLexicalHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlLocator.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlLocator.cc index 52084ecec..de37d7a3d 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlLocator.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlLocator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlNamespaceSupport.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlNamespaceSupport.cc index dea5d7cf9..8464efcee 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlNamespaceSupport.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlNamespaceSupport.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlParseException.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlParseException.cc index 093ee25a5..7da965ba9 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlParseException.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlParseException.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlReader.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlReader.cc index bca2e26ee..5d922113f 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlReader.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQXmlSimpleReader.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlSimpleReader.cc index 024d5f713..72a3dee4c 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlSimpleReader.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlSimpleReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiDeclQtXmlTypeTraits.h b/src/gsiqt/qt5/QtXml/gsiDeclQtXmlTypeTraits.h index be8df59ed..cbe91f760 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQtXmlTypeTraits.h +++ b/src/gsiqt/qt5/QtXml/gsiDeclQtXmlTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXml/gsiQtExternals.h b/src/gsiqt/qt5/QtXml/gsiQtExternals.h index 0a2223e04..a9519663a 100644 --- a/src/gsiqt/qt5/QtXml/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtXml/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractMessageHandler.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractMessageHandler.cc index b1ae896d2..c90757e67 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractMessageHandler.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractMessageHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractUriResolver.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractUriResolver.cc index 270cbd785..e2814e35b 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractUriResolver.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractUriResolver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractXmlNodeModel.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractXmlNodeModel.cc index 2355feb95..6fe4d8ffd 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractXmlNodeModel.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractXmlNodeModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractXmlReceiver.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractXmlReceiver.cc index b293c40cd..3ff153ec9 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractXmlReceiver.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQAbstractXmlReceiver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQSimpleXmlNodeModel.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQSimpleXmlNodeModel.cc index 3ee93b20a..734fb2180 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQSimpleXmlNodeModel.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQSimpleXmlNodeModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQSourceLocation.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQSourceLocation.cc index 940f7d35e..1c9190832 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQSourceLocation.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQSourceLocation.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlFormatter.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlFormatter.cc index ef63a9380..ddd285ac6 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlFormatter.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlFormatter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlItem.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlItem.cc index 2580dfd3b..6e548112c 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlItem.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlItem.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlName.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlName.cc index dcdbe8a78..a45d2e73f 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlName.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlName.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlNamePool.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlNamePool.cc index 723bddc1c..d44ba4b98 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlNamePool.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlNamePool.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlNodeModelIndex.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlNodeModelIndex.cc index dc940986b..b3a78c210 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlNodeModelIndex.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlNodeModelIndex.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlQuery.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlQuery.cc index adb90564a..0b91e1ac4 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlQuery.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlQuery.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlResultItems.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlResultItems.cc index 6bf5c0436..4715a124e 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlResultItems.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlResultItems.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSchema.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSchema.cc index 8aeee9ce8..6df211ab8 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSchema.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSchema.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSchemaValidator.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSchemaValidator.cc index 21382beba..2e12f21db 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSchemaValidator.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSchemaValidator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSerializer.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSerializer.cc index 8f57a462c..0f63ebf15 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSerializer.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQXmlSerializer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQtXmlPatternsAdd.cc b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQtXmlPatternsAdd.cc index d730ed254..291df6fc5 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQtXmlPatternsAdd.cc +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQtXmlPatternsAdd.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQtXmlPatternsTypeTraits.h b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQtXmlPatternsTypeTraits.h index d137fbde4..ae1d45b9e 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQtXmlPatternsTypeTraits.h +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiDeclQtXmlPatternsTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qt5/QtXmlPatterns/gsiQtExternals.h b/src/gsiqt/qt5/QtXmlPatterns/gsiQtExternals.h index b10fbd333..f8781f687 100644 --- a/src/gsiqt/qt5/QtXmlPatterns/gsiQtExternals.h +++ b/src/gsiqt/qt5/QtXmlPatterns/gsiQtExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiDeclQtAllTypeTraits.h b/src/gsiqt/qtbasic/gsiDeclQtAllTypeTraits.h index 0ac68bf7d..53dc00590 100644 --- a/src/gsiqt/qtbasic/gsiDeclQtAllTypeTraits.h +++ b/src/gsiqt/qtbasic/gsiDeclQtAllTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQt.cc b/src/gsiqt/qtbasic/gsiQt.cc index 5dfd3aa6f..526d22a4a 100644 --- a/src/gsiqt/qtbasic/gsiQt.cc +++ b/src/gsiqt/qtbasic/gsiQt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQt.h b/src/gsiqt/qtbasic/gsiQt.h index a7eb0b434..f0ef9b8ca 100644 --- a/src/gsiqt/qtbasic/gsiQt.h +++ b/src/gsiqt/qtbasic/gsiQt.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtBasicCommon.h b/src/gsiqt/qtbasic/gsiQtBasicCommon.h index 015ca44d7..b92e2ebf8 100644 --- a/src/gsiqt/qtbasic/gsiQtBasicCommon.h +++ b/src/gsiqt/qtbasic/gsiQtBasicCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtCoreExternals.h b/src/gsiqt/qtbasic/gsiQtCoreExternals.h index e4455db87..fea2053fb 100644 --- a/src/gsiqt/qtbasic/gsiQtCoreExternals.h +++ b/src/gsiqt/qtbasic/gsiQtCoreExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtDesignerExternals.h b/src/gsiqt/qtbasic/gsiQtDesignerExternals.h index e1c28a161..2bc83f74f 100644 --- a/src/gsiqt/qtbasic/gsiQtDesignerExternals.h +++ b/src/gsiqt/qtbasic/gsiQtDesignerExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtGuiExternals.h b/src/gsiqt/qtbasic/gsiQtGuiExternals.h index 2773c3da4..de99a7632 100644 --- a/src/gsiqt/qtbasic/gsiQtGuiExternals.h +++ b/src/gsiqt/qtbasic/gsiQtGuiExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtHelper.cc b/src/gsiqt/qtbasic/gsiQtHelper.cc index 1c530a5fa..ae6ff713c 100644 --- a/src/gsiqt/qtbasic/gsiQtHelper.cc +++ b/src/gsiqt/qtbasic/gsiQtHelper.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtHelper.h b/src/gsiqt/qtbasic/gsiQtHelper.h index 28333046a..86b5cc350 100644 --- a/src/gsiqt/qtbasic/gsiQtHelper.h +++ b/src/gsiqt/qtbasic/gsiQtHelper.h @@ -1,7 +1,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtMultimediaExternals.h b/src/gsiqt/qtbasic/gsiQtMultimediaExternals.h index e06e15135..af2117cb7 100644 --- a/src/gsiqt/qtbasic/gsiQtMultimediaExternals.h +++ b/src/gsiqt/qtbasic/gsiQtMultimediaExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtNetworkExternals.h b/src/gsiqt/qtbasic/gsiQtNetworkExternals.h index 003609d87..dfe2b8dd1 100644 --- a/src/gsiqt/qtbasic/gsiQtNetworkExternals.h +++ b/src/gsiqt/qtbasic/gsiQtNetworkExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtPrintSupportExternals.h b/src/gsiqt/qtbasic/gsiQtPrintSupportExternals.h index f372abccc..337cb8889 100644 --- a/src/gsiqt/qtbasic/gsiQtPrintSupportExternals.h +++ b/src/gsiqt/qtbasic/gsiQtPrintSupportExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtSqlExternals.h b/src/gsiqt/qtbasic/gsiQtSqlExternals.h index a0a45a4e2..4475b6314 100644 --- a/src/gsiqt/qtbasic/gsiQtSqlExternals.h +++ b/src/gsiqt/qtbasic/gsiQtSqlExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtSvgExternals.h b/src/gsiqt/qtbasic/gsiQtSvgExternals.h index 07edc0492..49cf7a2d0 100644 --- a/src/gsiqt/qtbasic/gsiQtSvgExternals.h +++ b/src/gsiqt/qtbasic/gsiQtSvgExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtWidgetsExternals.h b/src/gsiqt/qtbasic/gsiQtWidgetsExternals.h index 5188b45a6..82f0ada94 100644 --- a/src/gsiqt/qtbasic/gsiQtWidgetsExternals.h +++ b/src/gsiqt/qtbasic/gsiQtWidgetsExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtXmlExternals.h b/src/gsiqt/qtbasic/gsiQtXmlExternals.h index 317bd2fbe..1b77f5da2 100644 --- a/src/gsiqt/qtbasic/gsiQtXmlExternals.h +++ b/src/gsiqt/qtbasic/gsiQtXmlExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gsiqt/qtbasic/gsiQtXmlPatternsExternals.h b/src/gsiqt/qtbasic/gsiQtXmlPatternsExternals.h index 99431a57c..bb53e7376 100644 --- a/src/gsiqt/qtbasic/gsiQtXmlPatternsExternals.h +++ b/src/gsiqt/qtbasic/gsiQtXmlPatternsExternals.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gtfui/gtfUiDialog.cc b/src/gtfui/gtfUiDialog.cc index 70c32da38..5cfd169f3 100644 --- a/src/gtfui/gtfUiDialog.cc +++ b/src/gtfui/gtfUiDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gtfui/gtfUiDialog.h b/src/gtfui/gtfUiDialog.h index 2354512cb..e6c82eb66 100644 --- a/src/gtfui/gtfUiDialog.h +++ b/src/gtfui/gtfUiDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/gtfui/gtfui.cc b/src/gtfui/gtfui.cc index a3df0bb8c..bc89d96bf 100644 --- a/src/gtfui/gtfui.cc +++ b/src/gtfui/gtfui.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/gsiDeclImg.cc b/src/img/img/gsiDeclImg.cc index 00a07b69c..218a962bf 100644 --- a/src/img/img/gsiDeclImg.cc +++ b/src/img/img/gsiDeclImg.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgCommon.h b/src/img/img/imgCommon.h index 89022d433..9657e8ddb 100644 --- a/src/img/img/imgCommon.h +++ b/src/img/img/imgCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgForceLink.cc b/src/img/img/imgForceLink.cc index f4a7ccc3d..4d8873d47 100644 --- a/src/img/img/imgForceLink.cc +++ b/src/img/img/imgForceLink.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgForceLink.h b/src/img/img/imgForceLink.h index b8fb49133..143cf8379 100644 --- a/src/img/img/imgForceLink.h +++ b/src/img/img/imgForceLink.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgLandmarksDialog.cc b/src/img/img/imgLandmarksDialog.cc index c6f985bce..8d1bea5eb 100644 --- a/src/img/img/imgLandmarksDialog.cc +++ b/src/img/img/imgLandmarksDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgLandmarksDialog.h b/src/img/img/imgLandmarksDialog.h index 5f2060482..2c2c9e677 100644 --- a/src/img/img/imgLandmarksDialog.h +++ b/src/img/img/imgLandmarksDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgNavigator.cc b/src/img/img/imgNavigator.cc index 0ed421f84..13b3314b6 100644 --- a/src/img/img/imgNavigator.cc +++ b/src/img/img/imgNavigator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgNavigator.h b/src/img/img/imgNavigator.h index 3ab906306..d9d1afb86 100644 --- a/src/img/img/imgNavigator.h +++ b/src/img/img/imgNavigator.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgObject.cc b/src/img/img/imgObject.cc index 5e66c25cc..ad67989ed 100644 --- a/src/img/img/imgObject.cc +++ b/src/img/img/imgObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgObject.h b/src/img/img/imgObject.h index 362b896d0..c7b975f75 100644 --- a/src/img/img/imgObject.h +++ b/src/img/img/imgObject.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgPlugin.cc b/src/img/img/imgPlugin.cc index 7cf0a26be..a8c60e930 100644 --- a/src/img/img/imgPlugin.cc +++ b/src/img/img/imgPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgPlugin.h b/src/img/img/imgPlugin.h index a8d59ae64..e4ea182b3 100644 --- a/src/img/img/imgPlugin.h +++ b/src/img/img/imgPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index eb8636e86..870fb55aa 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgPropertiesPage.h b/src/img/img/imgPropertiesPage.h index 3462b8737..66517c166 100644 --- a/src/img/img/imgPropertiesPage.h +++ b/src/img/img/imgPropertiesPage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index 57e052f0d..5c2519d69 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgService.h b/src/img/img/imgService.h index 1724a0cea..b689e8a63 100644 --- a/src/img/img/imgService.h +++ b/src/img/img/imgService.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgWidgets.cc b/src/img/img/imgWidgets.cc index 219b5d4d6..4f320e204 100644 --- a/src/img/img/imgWidgets.cc +++ b/src/img/img/imgWidgets.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/img/imgWidgets.h b/src/img/img/imgWidgets.h index d7e035d3c..631b28df4 100644 --- a/src/img/img/imgWidgets.h +++ b/src/img/img/imgWidgets.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/img/unit_tests/imgObject.cc b/src/img/unit_tests/imgObject.cc index e738f31b2..32e1affb1 100644 --- a/src/img/unit_tests/imgObject.cc +++ b/src/img/unit_tests/imgObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/klayout_main/klayout_main/klayout.cc b/src/klayout_main/klayout_main/klayout.cc index 0e90505c4..29187840f 100644 --- a/src/klayout_main/klayout_main/klayout.cc +++ b/src/klayout_main/klayout_main/klayout.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/klayout_main/tests/klayout_main_tests.cc b/src/klayout_main/tests/klayout_main_tests.cc index a1cffbb3a..5fa3afacf 100644 --- a/src/klayout_main/tests/klayout_main_tests.cc +++ b/src/klayout_main/tests/klayout_main_tests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/gsiDeclLayApplication.cc b/src/lay/lay/gsiDeclLayApplication.cc index c5e97bc15..fbd8e9375 100644 --- a/src/lay/lay/gsiDeclLayApplication.cc +++ b/src/lay/lay/gsiDeclLayApplication.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/gsiDeclLayHelpDialog.cc b/src/lay/lay/gsiDeclLayHelpDialog.cc index 980249fdb..1a7554f05 100644 --- a/src/lay/lay/gsiDeclLayHelpDialog.cc +++ b/src/lay/lay/gsiDeclLayHelpDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/gsiDeclLayMainWindow.cc b/src/lay/lay/gsiDeclLayMainWindow.cc index c5ac84bc2..b04f54f37 100644 --- a/src/lay/lay/gsiDeclLayMainWindow.cc +++ b/src/lay/lay/gsiDeclLayMainWindow.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layApplication.cc b/src/lay/lay/layApplication.cc index 76da61364..412da9d3d 100644 --- a/src/lay/lay/layApplication.cc +++ b/src/lay/lay/layApplication.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layApplication.h b/src/lay/lay/layApplication.h index c49e3b7b5..6d8224498 100644 --- a/src/lay/lay/layApplication.h +++ b/src/lay/lay/layApplication.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layClipDialog.cc b/src/lay/lay/layClipDialog.cc index 8071f8df1..6bdc434f8 100644 --- a/src/lay/lay/layClipDialog.cc +++ b/src/lay/lay/layClipDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layClipDialog.h b/src/lay/lay/layClipDialog.h index f6b5f5fbc..7ecc0d13c 100644 --- a/src/lay/lay/layClipDialog.h +++ b/src/lay/lay/layClipDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layCommon.h b/src/lay/lay/layCommon.h index 687866310..6504b647b 100644 --- a/src/lay/lay/layCommon.h +++ b/src/lay/lay/layCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layConfig.h b/src/lay/lay/layConfig.h index 0aa89ed45..ade67ad0b 100644 --- a/src/lay/lay/layConfig.h +++ b/src/lay/lay/layConfig.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layCrashMessage.cc b/src/lay/lay/layCrashMessage.cc index 1a83ab1c8..0bdfbc083 100644 --- a/src/lay/lay/layCrashMessage.cc +++ b/src/lay/lay/layCrashMessage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layCrashMessage.h b/src/lay/lay/layCrashMessage.h index df8179370..8cceb9a76 100644 --- a/src/lay/lay/layCrashMessage.h +++ b/src/lay/lay/layCrashMessage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layFillDialog.cc b/src/lay/lay/layFillDialog.cc index 4968e86c4..3b82c4856 100644 --- a/src/lay/lay/layFillDialog.cc +++ b/src/lay/lay/layFillDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layFillDialog.h b/src/lay/lay/layFillDialog.h index 8526b0710..59a4753f0 100644 --- a/src/lay/lay/layFillDialog.h +++ b/src/lay/lay/layFillDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layFontController.cc b/src/lay/lay/layFontController.cc index 40f5014dc..17a2ae16d 100644 --- a/src/lay/lay/layFontController.cc +++ b/src/lay/lay/layFontController.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layFontController.h b/src/lay/lay/layFontController.h index 508c2c4ba..39df0b4bc 100644 --- a/src/lay/lay/layFontController.h +++ b/src/lay/lay/layFontController.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layForceLink.cc b/src/lay/lay/layForceLink.cc index 65988510c..fa742f9d0 100644 --- a/src/lay/lay/layForceLink.cc +++ b/src/lay/lay/layForceLink.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layForceLink.h b/src/lay/lay/layForceLink.h index b107d99fe..607f480f9 100644 --- a/src/lay/lay/layForceLink.h +++ b/src/lay/lay/layForceLink.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layGSIHelpProvider.cc b/src/lay/lay/layGSIHelpProvider.cc index e77efe6b5..75f32bb36 100644 --- a/src/lay/lay/layGSIHelpProvider.cc +++ b/src/lay/lay/layGSIHelpProvider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layGSIHelpProvider.h b/src/lay/lay/layGSIHelpProvider.h index ee7f5f622..32e856454 100644 --- a/src/lay/lay/layGSIHelpProvider.h +++ b/src/lay/lay/layGSIHelpProvider.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layGenericSyntaxHighlighter.cc b/src/lay/lay/layGenericSyntaxHighlighter.cc index e1324be73..10e786f90 100644 --- a/src/lay/lay/layGenericSyntaxHighlighter.cc +++ b/src/lay/lay/layGenericSyntaxHighlighter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layGenericSyntaxHighlighter.h b/src/lay/lay/layGenericSyntaxHighlighter.h index 2bcbefa2e..aa897e7fb 100644 --- a/src/lay/lay/layGenericSyntaxHighlighter.h +++ b/src/lay/lay/layGenericSyntaxHighlighter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layHelpDialog.cc b/src/lay/lay/layHelpDialog.cc index 1371be842..9a0010a69 100644 --- a/src/lay/lay/layHelpDialog.cc +++ b/src/lay/lay/layHelpDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layHelpDialog.h b/src/lay/lay/layHelpDialog.h index ff860d7e7..00769b610 100644 --- a/src/lay/lay/layHelpDialog.h +++ b/src/lay/lay/layHelpDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layHelpProvider.cc b/src/lay/lay/layHelpProvider.cc index 462dcb61c..a56fba857 100644 --- a/src/lay/lay/layHelpProvider.cc +++ b/src/lay/lay/layHelpProvider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layHelpProvider.h b/src/lay/lay/layHelpProvider.h index 200bfb286..4083d06fb 100644 --- a/src/lay/lay/layHelpProvider.h +++ b/src/lay/lay/layHelpProvider.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layHelpSource.cc b/src/lay/lay/layHelpSource.cc index ff8a53e3f..b1378a0a2 100644 --- a/src/lay/lay/layHelpSource.cc +++ b/src/lay/lay/layHelpSource.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layHelpSource.h b/src/lay/lay/layHelpSource.h index b7afcd179..b58555a01 100644 --- a/src/lay/lay/layHelpSource.h +++ b/src/lay/lay/layHelpSource.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layInit.cc b/src/lay/lay/layInit.cc index 4262cb1f3..d098672e6 100644 --- a/src/lay/lay/layInit.cc +++ b/src/lay/lay/layInit.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layInit.h b/src/lay/lay/layInit.h index 3a89e0d02..38c230090 100644 --- a/src/lay/lay/layInit.h +++ b/src/lay/lay/layInit.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layLayoutStatisticsForm.cc b/src/lay/lay/layLayoutStatisticsForm.cc index fa55815e0..ff8bc53ff 100644 --- a/src/lay/lay/layLayoutStatisticsForm.cc +++ b/src/lay/lay/layLayoutStatisticsForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layLayoutStatisticsForm.h b/src/lay/lay/layLayoutStatisticsForm.h index aed1154b1..3c8d32c7e 100644 --- a/src/lay/lay/layLayoutStatisticsForm.h +++ b/src/lay/lay/layLayoutStatisticsForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index 065c24b61..992ee231d 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layLibraryController.h b/src/lay/lay/layLibraryController.h index 9ea5503bb..17b867a55 100644 --- a/src/lay/lay/layLibraryController.h +++ b/src/lay/lay/layLibraryController.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layLogViewerDialog.cc b/src/lay/lay/layLogViewerDialog.cc index 077f26a7e..3d037d6f0 100644 --- a/src/lay/lay/layLogViewerDialog.cc +++ b/src/lay/lay/layLogViewerDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layLogViewerDialog.h b/src/lay/lay/layLogViewerDialog.h index 22a00a2f2..2ff2608ef 100644 --- a/src/lay/lay/layLogViewerDialog.h +++ b/src/lay/lay/layLogViewerDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroController.cc b/src/lay/lay/layMacroController.cc index 4ea5776cc..202e91756 100644 --- a/src/lay/lay/layMacroController.cc +++ b/src/lay/lay/layMacroController.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroController.h b/src/lay/lay/layMacroController.h index cf0776885..4859e9a42 100644 --- a/src/lay/lay/layMacroController.h +++ b/src/lay/lay/layMacroController.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroEditorDialog.cc b/src/lay/lay/layMacroEditorDialog.cc index 926400848..c10fafcd7 100644 --- a/src/lay/lay/layMacroEditorDialog.cc +++ b/src/lay/lay/layMacroEditorDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroEditorDialog.h b/src/lay/lay/layMacroEditorDialog.h index e1fea9391..fd634fbc8 100644 --- a/src/lay/lay/layMacroEditorDialog.h +++ b/src/lay/lay/layMacroEditorDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroEditorPage.cc b/src/lay/lay/layMacroEditorPage.cc index b55753fb0..b7fad77ec 100644 --- a/src/lay/lay/layMacroEditorPage.cc +++ b/src/lay/lay/layMacroEditorPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroEditorPage.h b/src/lay/lay/layMacroEditorPage.h index a15b596ff..0142d76c6 100644 --- a/src/lay/lay/layMacroEditorPage.h +++ b/src/lay/lay/layMacroEditorPage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroEditorSetupPage.cc b/src/lay/lay/layMacroEditorSetupPage.cc index 2b9237358..52a098213 100644 --- a/src/lay/lay/layMacroEditorSetupPage.cc +++ b/src/lay/lay/layMacroEditorSetupPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroEditorSetupPage.h b/src/lay/lay/layMacroEditorSetupPage.h index 85f763ef8..2fa240ec5 100644 --- a/src/lay/lay/layMacroEditorSetupPage.h +++ b/src/lay/lay/layMacroEditorSetupPage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroEditorTree.cc b/src/lay/lay/layMacroEditorTree.cc index 032b76ab1..20d389ea9 100644 --- a/src/lay/lay/layMacroEditorTree.cc +++ b/src/lay/lay/layMacroEditorTree.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroEditorTree.h b/src/lay/lay/layMacroEditorTree.h index 53324178a..0b2e3fe93 100644 --- a/src/lay/lay/layMacroEditorTree.h +++ b/src/lay/lay/layMacroEditorTree.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroPropertiesDialog.cc b/src/lay/lay/layMacroPropertiesDialog.cc index dd76da167..ed484018f 100644 --- a/src/lay/lay/layMacroPropertiesDialog.cc +++ b/src/lay/lay/layMacroPropertiesDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroPropertiesDialog.h b/src/lay/lay/layMacroPropertiesDialog.h index ac3ef591b..c567bd70a 100644 --- a/src/lay/lay/layMacroPropertiesDialog.h +++ b/src/lay/lay/layMacroPropertiesDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroVariableView.cc b/src/lay/lay/layMacroVariableView.cc index d80eae8ac..3f96365d3 100644 --- a/src/lay/lay/layMacroVariableView.cc +++ b/src/lay/lay/layMacroVariableView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMacroVariableView.h b/src/lay/lay/layMacroVariableView.h index 00f6bcf40..5a0bd9576 100644 --- a/src/lay/lay/layMacroVariableView.h +++ b/src/lay/lay/layMacroVariableView.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index b244bd3d0..25e2bbd4a 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMainConfigPages.h b/src/lay/lay/layMainConfigPages.h index b3a37b7dc..64212860a 100644 --- a/src/lay/lay/layMainConfigPages.h +++ b/src/lay/lay/layMainConfigPages.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 352f6e504..37f54d562 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 8bb84bd04..2d18a1d01 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layNativePlugin.cc b/src/lay/lay/layNativePlugin.cc index a81c33b27..7cbcef5e1 100644 --- a/src/lay/lay/layNativePlugin.cc +++ b/src/lay/lay/layNativePlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layNativePlugin.h b/src/lay/lay/layNativePlugin.h index 705258761..deeffb490 100644 --- a/src/lay/lay/layNativePlugin.h +++ b/src/lay/lay/layNativePlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layNavigator.cc b/src/lay/lay/layNavigator.cc index 1b4ec8119..d56f61984 100644 --- a/src/lay/lay/layNavigator.cc +++ b/src/lay/lay/layNavigator.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layNavigator.h b/src/lay/lay/layNavigator.h index 5582eea8b..af79ec241 100644 --- a/src/lay/lay/layNavigator.h +++ b/src/lay/lay/layNavigator.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layPasswordDialog.cc b/src/lay/lay/layPasswordDialog.cc index dfc6341d9..df89d7fc6 100644 --- a/src/lay/lay/layPasswordDialog.cc +++ b/src/lay/lay/layPasswordDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layPasswordDialog.h b/src/lay/lay/layPasswordDialog.h index 7b20d4fbf..4b2e5fc74 100644 --- a/src/lay/lay/layPasswordDialog.h +++ b/src/lay/lay/layPasswordDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layProgress.cc b/src/lay/lay/layProgress.cc index 1a0af1d0a..5303692ea 100644 --- a/src/lay/lay/layProgress.cc +++ b/src/lay/lay/layProgress.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layProgress.h b/src/lay/lay/layProgress.h index a84549535..c7a6361f7 100644 --- a/src/lay/lay/layProgress.h +++ b/src/lay/lay/layProgress.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layProgressWidget.cc b/src/lay/lay/layProgressWidget.cc index 5351c0821..36a76d266 100644 --- a/src/lay/lay/layProgressWidget.cc +++ b/src/lay/lay/layProgressWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layProgressWidget.h b/src/lay/lay/layProgressWidget.h index e1606b948..976e799fd 100644 --- a/src/lay/lay/layProgressWidget.h +++ b/src/lay/lay/layProgressWidget.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layResourceHelpProvider.cc b/src/lay/lay/layResourceHelpProvider.cc index 61fe8deae..130b28168 100644 --- a/src/lay/lay/layResourceHelpProvider.cc +++ b/src/lay/lay/layResourceHelpProvider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layResourceHelpProvider.h b/src/lay/lay/layResourceHelpProvider.h index 78a77519b..5ecdd738b 100644 --- a/src/lay/lay/layResourceHelpProvider.h +++ b/src/lay/lay/layResourceHelpProvider.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layRuntimeErrorForm.cc b/src/lay/lay/layRuntimeErrorForm.cc index aa20bfd2c..e338e9d76 100644 --- a/src/lay/lay/layRuntimeErrorForm.cc +++ b/src/lay/lay/layRuntimeErrorForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layRuntimeErrorForm.h b/src/lay/lay/layRuntimeErrorForm.h index 7ef3331ed..bc0d69cb7 100644 --- a/src/lay/lay/layRuntimeErrorForm.h +++ b/src/lay/lay/layRuntimeErrorForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySalt.cc b/src/lay/lay/laySalt.cc index 51bfa0b0d..295547a4b 100644 --- a/src/lay/lay/laySalt.cc +++ b/src/lay/lay/laySalt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySalt.h b/src/lay/lay/laySalt.h index 3bd170e0d..a08027e4b 100644 --- a/src/lay/lay/laySalt.h +++ b/src/lay/lay/laySalt.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltController.cc b/src/lay/lay/laySaltController.cc index 4f6b78815..d9fbb5e64 100644 --- a/src/lay/lay/laySaltController.cc +++ b/src/lay/lay/laySaltController.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltController.h b/src/lay/lay/laySaltController.h index ce8ee0e9b..a062c144f 100644 --- a/src/lay/lay/laySaltController.h +++ b/src/lay/lay/laySaltController.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltDownloadManager.cc b/src/lay/lay/laySaltDownloadManager.cc index b2ca7633c..9f3298756 100644 --- a/src/lay/lay/laySaltDownloadManager.cc +++ b/src/lay/lay/laySaltDownloadManager.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltDownloadManager.h b/src/lay/lay/laySaltDownloadManager.h index 749347c56..57de7f287 100644 --- a/src/lay/lay/laySaltDownloadManager.h +++ b/src/lay/lay/laySaltDownloadManager.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltGrain.cc b/src/lay/lay/laySaltGrain.cc index 3ab886ebb..fbf3f1a3c 100644 --- a/src/lay/lay/laySaltGrain.cc +++ b/src/lay/lay/laySaltGrain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltGrain.h b/src/lay/lay/laySaltGrain.h index 81e052293..b0df65f81 100644 --- a/src/lay/lay/laySaltGrain.h +++ b/src/lay/lay/laySaltGrain.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltGrainDetailsTextWidget.cc b/src/lay/lay/laySaltGrainDetailsTextWidget.cc index 3988b2e43..16f07e53b 100644 --- a/src/lay/lay/laySaltGrainDetailsTextWidget.cc +++ b/src/lay/lay/laySaltGrainDetailsTextWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltGrainDetailsTextWidget.h b/src/lay/lay/laySaltGrainDetailsTextWidget.h index 51a355031..e2906c796 100644 --- a/src/lay/lay/laySaltGrainDetailsTextWidget.h +++ b/src/lay/lay/laySaltGrainDetailsTextWidget.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltGrainPropertiesDialog.cc b/src/lay/lay/laySaltGrainPropertiesDialog.cc index 619a8ee81..f0202aa29 100644 --- a/src/lay/lay/laySaltGrainPropertiesDialog.cc +++ b/src/lay/lay/laySaltGrainPropertiesDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltGrainPropertiesDialog.h b/src/lay/lay/laySaltGrainPropertiesDialog.h index bbf8c7d20..f84072aee 100644 --- a/src/lay/lay/laySaltGrainPropertiesDialog.h +++ b/src/lay/lay/laySaltGrainPropertiesDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltGrains.cc b/src/lay/lay/laySaltGrains.cc index f414c36c5..e58d0c7e7 100644 --- a/src/lay/lay/laySaltGrains.cc +++ b/src/lay/lay/laySaltGrains.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltGrains.h b/src/lay/lay/laySaltGrains.h index 6d2fb1ebe..04c384947 100644 --- a/src/lay/lay/laySaltGrains.h +++ b/src/lay/lay/laySaltGrains.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltManagerDialog.cc b/src/lay/lay/laySaltManagerDialog.cc index 68639c821..9740a9874 100644 --- a/src/lay/lay/laySaltManagerDialog.cc +++ b/src/lay/lay/laySaltManagerDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltManagerDialog.h b/src/lay/lay/laySaltManagerDialog.h index 8d1687ccf..6d1ba5aa2 100644 --- a/src/lay/lay/laySaltManagerDialog.h +++ b/src/lay/lay/laySaltManagerDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltModel.cc b/src/lay/lay/laySaltModel.cc index 3cce6c3a9..c3be49c5d 100644 --- a/src/lay/lay/laySaltModel.cc +++ b/src/lay/lay/laySaltModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySaltModel.h b/src/lay/lay/laySaltModel.h index 4447bad7b..d0714a644 100644 --- a/src/lay/lay/laySaltModel.h +++ b/src/lay/lay/laySaltModel.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySearchReplaceConfigPage.cc b/src/lay/lay/laySearchReplaceConfigPage.cc index e3540d720..d633277b6 100644 --- a/src/lay/lay/laySearchReplaceConfigPage.cc +++ b/src/lay/lay/laySearchReplaceConfigPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySearchReplaceConfigPage.h b/src/lay/lay/laySearchReplaceConfigPage.h index 2233f5e28..65cf34dff 100644 --- a/src/lay/lay/laySearchReplaceConfigPage.h +++ b/src/lay/lay/laySearchReplaceConfigPage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index 499c8c212..5a45b124e 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySearchReplaceDialog.h b/src/lay/lay/laySearchReplaceDialog.h index 558f9b15d..55da7d131 100644 --- a/src/lay/lay/laySearchReplaceDialog.h +++ b/src/lay/lay/laySearchReplaceDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySearchReplacePlugin.cc b/src/lay/lay/laySearchReplacePlugin.cc index c2182d8cd..5e076e014 100644 --- a/src/lay/lay/laySearchReplacePlugin.cc +++ b/src/lay/lay/laySearchReplacePlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySearchReplacePropertiesWidgets.cc b/src/lay/lay/laySearchReplacePropertiesWidgets.cc index e374b4a05..3af023b41 100644 --- a/src/lay/lay/laySearchReplacePropertiesWidgets.cc +++ b/src/lay/lay/laySearchReplacePropertiesWidgets.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySearchReplacePropertiesWidgets.h b/src/lay/lay/laySearchReplacePropertiesWidgets.h index 76c474c63..290556d0f 100644 --- a/src/lay/lay/laySearchReplacePropertiesWidgets.h +++ b/src/lay/lay/laySearchReplacePropertiesWidgets.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySelectCellViewForm.cc b/src/lay/lay/laySelectCellViewForm.cc index cbf25b221..78239a7ca 100644 --- a/src/lay/lay/laySelectCellViewForm.cc +++ b/src/lay/lay/laySelectCellViewForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySelectCellViewForm.h b/src/lay/lay/laySelectCellViewForm.h index 3327c81c4..17838d2d0 100644 --- a/src/lay/lay/laySelectCellViewForm.h +++ b/src/lay/lay/laySelectCellViewForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySession.cc b/src/lay/lay/laySession.cc index 67f5785f1..f834856e2 100644 --- a/src/lay/lay/laySession.cc +++ b/src/lay/lay/laySession.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySession.h b/src/lay/lay/laySession.h index d0c2fbaef..e2ae4a9a9 100644 --- a/src/lay/lay/laySession.h +++ b/src/lay/lay/laySession.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySettingsForm.cc b/src/lay/lay/laySettingsForm.cc index 5e9649cd0..48536e27c 100644 --- a/src/lay/lay/laySettingsForm.cc +++ b/src/lay/lay/laySettingsForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySettingsForm.h b/src/lay/lay/laySettingsForm.h index 1295b03cb..7b8ce4e66 100644 --- a/src/lay/lay/laySettingsForm.h +++ b/src/lay/lay/laySettingsForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySignalHandler.cc b/src/lay/lay/laySignalHandler.cc index 12f540232..ea2ee0939 100644 --- a/src/lay/lay/laySignalHandler.cc +++ b/src/lay/lay/laySignalHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySignalHandler.h b/src/lay/lay/laySignalHandler.h index 351013442..18d45e1d8 100644 --- a/src/lay/lay/laySignalHandler.h +++ b/src/lay/lay/laySignalHandler.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySystemPaths.cc b/src/lay/lay/laySystemPaths.cc index 0943c0f24..37b8ab81e 100644 --- a/src/lay/lay/laySystemPaths.cc +++ b/src/lay/lay/laySystemPaths.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/laySystemPaths.h b/src/lay/lay/laySystemPaths.h index 790c0ced7..990b0a2fe 100644 --- a/src/lay/lay/laySystemPaths.h +++ b/src/lay/lay/laySystemPaths.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layTechSetupDialog.cc b/src/lay/lay/layTechSetupDialog.cc index 35b12eb23..89454c5d1 100644 --- a/src/lay/lay/layTechSetupDialog.cc +++ b/src/lay/lay/layTechSetupDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layTechSetupDialog.h b/src/lay/lay/layTechSetupDialog.h index e1e88ea2d..e680212f8 100644 --- a/src/lay/lay/layTechSetupDialog.h +++ b/src/lay/lay/layTechSetupDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layTechnologyController.cc b/src/lay/lay/layTechnologyController.cc index 8696e4f45..f6ca65ee5 100644 --- a/src/lay/lay/layTechnologyController.cc +++ b/src/lay/lay/layTechnologyController.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layTechnologyController.h b/src/lay/lay/layTechnologyController.h index dc347204b..094b94bb3 100644 --- a/src/lay/lay/layTechnologyController.h +++ b/src/lay/lay/layTechnologyController.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layTextProgress.cc b/src/lay/lay/layTextProgress.cc index a466b933c..1f9539f8b 100644 --- a/src/lay/lay/layTextProgress.cc +++ b/src/lay/lay/layTextProgress.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layTextProgress.h b/src/lay/lay/layTextProgress.h index 23a0638b7..c1d5cf12f 100644 --- a/src/lay/lay/layTextProgress.h +++ b/src/lay/lay/layTextProgress.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layVersion.cc b/src/lay/lay/layVersion.cc index 323a3a74c..f2d56d3fb 100644 --- a/src/lay/lay/layVersion.cc +++ b/src/lay/lay/layVersion.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/lay/layVersion.h b/src/lay/lay/layVersion.h index 2ab56f194..d6d5c2909 100644 --- a/src/lay/lay/layVersion.h +++ b/src/lay/lay/layVersion.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lay/unit_tests/laySalt.cc b/src/lay/unit_tests/laySalt.cc index 9618a4410..91192f483 100644 --- a/src/lay/unit_tests/laySalt.cc +++ b/src/lay/unit_tests/laySalt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/gsiDeclLayDialogs.cc b/src/laybasic/laybasic/gsiDeclLayDialogs.cc index 050820b45..2d05e86d4 100644 --- a/src/laybasic/laybasic/gsiDeclLayDialogs.cc +++ b/src/laybasic/laybasic/gsiDeclLayDialogs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/gsiDeclLayLayers.cc b/src/laybasic/laybasic/gsiDeclLayLayers.cc index 3f432de67..9b49a9036 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayers.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayers.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc index ae70b30e1..a140bd1d3 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/gsiDeclLayMarker.cc b/src/laybasic/laybasic/gsiDeclLayMarker.cc index 0b78ce91d..44d75e0d8 100644 --- a/src/laybasic/laybasic/gsiDeclLayMarker.cc +++ b/src/laybasic/laybasic/gsiDeclLayMarker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/gsiDeclLayMenu.cc b/src/laybasic/laybasic/gsiDeclLayMenu.cc index e6b98e99a..6f201ff46 100644 --- a/src/laybasic/laybasic/gsiDeclLayMenu.cc +++ b/src/laybasic/laybasic/gsiDeclLayMenu.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/gsiDeclLayPlugin.cc b/src/laybasic/laybasic/gsiDeclLayPlugin.cc index b83cc3603..b14b279f4 100644 --- a/src/laybasic/laybasic/gsiDeclLayPlugin.cc +++ b/src/laybasic/laybasic/gsiDeclLayPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/gsiDeclLayStream.cc b/src/laybasic/laybasic/gsiDeclLayStream.cc index 3fa528b73..b99c3701c 100644 --- a/src/laybasic/laybasic/gsiDeclLayStream.cc +++ b/src/laybasic/laybasic/gsiDeclLayStream.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/gtf.cc b/src/laybasic/laybasic/gtf.cc index 7251a08ef..fa69f162e 100644 --- a/src/laybasic/laybasic/gtf.cc +++ b/src/laybasic/laybasic/gtf.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/gtf.h b/src/laybasic/laybasic/gtf.h index b3af7b328..f0db5eeeb 100644 --- a/src/laybasic/laybasic/gtf.h +++ b/src/laybasic/laybasic/gtf.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/gtfdummy.cc b/src/laybasic/laybasic/gtfdummy.cc index 22a3e02b1..7caab873a 100644 --- a/src/laybasic/laybasic/gtfdummy.cc +++ b/src/laybasic/laybasic/gtfdummy.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layAbstractMenu.cc b/src/laybasic/laybasic/layAbstractMenu.cc index dccd1ea33..de2ea1159 100644 --- a/src/laybasic/laybasic/layAbstractMenu.cc +++ b/src/laybasic/laybasic/layAbstractMenu.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layAbstractMenu.h b/src/laybasic/laybasic/layAbstractMenu.h index c5a6ade53..6c043987e 100644 --- a/src/laybasic/laybasic/layAbstractMenu.h +++ b/src/laybasic/laybasic/layAbstractMenu.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layAbstractMenuProvider.cc b/src/laybasic/laybasic/layAbstractMenuProvider.cc index e928bb318..f050424b8 100644 --- a/src/laybasic/laybasic/layAbstractMenuProvider.cc +++ b/src/laybasic/laybasic/layAbstractMenuProvider.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layAbstractMenuProvider.h b/src/laybasic/laybasic/layAbstractMenuProvider.h index 7fc5d7754..cbced1603 100644 --- a/src/laybasic/laybasic/layAbstractMenuProvider.h +++ b/src/laybasic/laybasic/layAbstractMenuProvider.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layAnnotationShapes.cc b/src/laybasic/laybasic/layAnnotationShapes.cc index 80444373d..d38f9a095 100644 --- a/src/laybasic/laybasic/layAnnotationShapes.cc +++ b/src/laybasic/laybasic/layAnnotationShapes.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layAnnotationShapes.h b/src/laybasic/laybasic/layAnnotationShapes.h index c0663f5e0..62cd8b02f 100644 --- a/src/laybasic/laybasic/layAnnotationShapes.h +++ b/src/laybasic/laybasic/layAnnotationShapes.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBackgroundAwareTreeStyle.cc b/src/laybasic/laybasic/layBackgroundAwareTreeStyle.cc index 23bcd0f00..4c2ea3cc9 100644 --- a/src/laybasic/laybasic/layBackgroundAwareTreeStyle.cc +++ b/src/laybasic/laybasic/layBackgroundAwareTreeStyle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBackgroundAwareTreeStyle.h b/src/laybasic/laybasic/layBackgroundAwareTreeStyle.h index 119efdb57..d336a9cd8 100644 --- a/src/laybasic/laybasic/layBackgroundAwareTreeStyle.h +++ b/src/laybasic/laybasic/layBackgroundAwareTreeStyle.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBitmap.cc b/src/laybasic/laybasic/layBitmap.cc index c2a41dece..d14610bd7 100644 --- a/src/laybasic/laybasic/layBitmap.cc +++ b/src/laybasic/laybasic/layBitmap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBitmap.h b/src/laybasic/laybasic/layBitmap.h index a1257d0d7..3476e31b7 100644 --- a/src/laybasic/laybasic/layBitmap.h +++ b/src/laybasic/laybasic/layBitmap.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBitmapRenderer.cc b/src/laybasic/laybasic/layBitmapRenderer.cc index 2b5b2ab70..7259551fb 100644 --- a/src/laybasic/laybasic/layBitmapRenderer.cc +++ b/src/laybasic/laybasic/layBitmapRenderer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBitmapRenderer.h b/src/laybasic/laybasic/layBitmapRenderer.h index c2dce8a26..43f3aba01 100644 --- a/src/laybasic/laybasic/layBitmapRenderer.h +++ b/src/laybasic/laybasic/layBitmapRenderer.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBitmapsToImage.cc b/src/laybasic/laybasic/layBitmapsToImage.cc index dbd8bbc64..2399397be 100644 --- a/src/laybasic/laybasic/layBitmapsToImage.cc +++ b/src/laybasic/laybasic/layBitmapsToImage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBitmapsToImage.h b/src/laybasic/laybasic/layBitmapsToImage.h index 8b45f2565..fd850833d 100644 --- a/src/laybasic/laybasic/layBitmapsToImage.h +++ b/src/laybasic/laybasic/layBitmapsToImage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBookmarkList.cc b/src/laybasic/laybasic/layBookmarkList.cc index 396899e75..a5e21817d 100644 --- a/src/laybasic/laybasic/layBookmarkList.cc +++ b/src/laybasic/laybasic/layBookmarkList.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBookmarkList.h b/src/laybasic/laybasic/layBookmarkList.h index 90190538c..6d9051642 100644 --- a/src/laybasic/laybasic/layBookmarkList.h +++ b/src/laybasic/laybasic/layBookmarkList.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBookmarkManagementForm.cc b/src/laybasic/laybasic/layBookmarkManagementForm.cc index 0806b65de..c1dace6e2 100644 --- a/src/laybasic/laybasic/layBookmarkManagementForm.cc +++ b/src/laybasic/laybasic/layBookmarkManagementForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBookmarkManagementForm.h b/src/laybasic/laybasic/layBookmarkManagementForm.h index 844733488..093697a82 100644 --- a/src/laybasic/laybasic/layBookmarkManagementForm.h +++ b/src/laybasic/laybasic/layBookmarkManagementForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBrowseInstancesForm.cc b/src/laybasic/laybasic/layBrowseInstancesForm.cc index 31c7e9261..d2e52f931 100644 --- a/src/laybasic/laybasic/layBrowseInstancesForm.cc +++ b/src/laybasic/laybasic/layBrowseInstancesForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBrowseInstancesForm.h b/src/laybasic/laybasic/layBrowseInstancesForm.h index faf256d28..3d810f338 100644 --- a/src/laybasic/laybasic/layBrowseInstancesForm.h +++ b/src/laybasic/laybasic/layBrowseInstancesForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBrowseShapesForm.cc b/src/laybasic/laybasic/layBrowseShapesForm.cc index 42c1f2f59..48a4bd673 100644 --- a/src/laybasic/laybasic/layBrowseShapesForm.cc +++ b/src/laybasic/laybasic/layBrowseShapesForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBrowseShapesForm.h b/src/laybasic/laybasic/layBrowseShapesForm.h index baa81ace6..98f680db4 100644 --- a/src/laybasic/laybasic/layBrowseShapesForm.h +++ b/src/laybasic/laybasic/layBrowseShapesForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBrowser.cc b/src/laybasic/laybasic/layBrowser.cc index a74f8f01d..92acf56dc 100644 --- a/src/laybasic/laybasic/layBrowser.cc +++ b/src/laybasic/laybasic/layBrowser.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBrowser.h b/src/laybasic/laybasic/layBrowser.h index bbbd08308..c371a2c94 100644 --- a/src/laybasic/laybasic/layBrowser.h +++ b/src/laybasic/laybasic/layBrowser.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBrowserDialog.cc b/src/laybasic/laybasic/layBrowserDialog.cc index b4666447a..527118dd9 100644 --- a/src/laybasic/laybasic/layBrowserDialog.cc +++ b/src/laybasic/laybasic/layBrowserDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBrowserDialog.h b/src/laybasic/laybasic/layBrowserDialog.h index dbe3d9acb..32f2a6718 100644 --- a/src/laybasic/laybasic/layBrowserDialog.h +++ b/src/laybasic/laybasic/layBrowserDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBrowserPanel.cc b/src/laybasic/laybasic/layBrowserPanel.cc index 6138b2b75..fbde23243 100644 --- a/src/laybasic/laybasic/layBrowserPanel.cc +++ b/src/laybasic/laybasic/layBrowserPanel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layBrowserPanel.h b/src/laybasic/laybasic/layBrowserPanel.h index 2551d4dc0..4d94fcc8d 100644 --- a/src/laybasic/laybasic/layBrowserPanel.h +++ b/src/laybasic/laybasic/layBrowserPanel.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layCanvasPlane.cc b/src/laybasic/laybasic/layCanvasPlane.cc index c84c2f2e1..7ae77d30d 100644 --- a/src/laybasic/laybasic/layCanvasPlane.cc +++ b/src/laybasic/laybasic/layCanvasPlane.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layCanvasPlane.h b/src/laybasic/laybasic/layCanvasPlane.h index 3017ff6c3..9854f09db 100644 --- a/src/laybasic/laybasic/layCanvasPlane.h +++ b/src/laybasic/laybasic/layCanvasPlane.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layCellSelectionForm.cc b/src/laybasic/laybasic/layCellSelectionForm.cc index 5be2d0962..8ce58c1b8 100644 --- a/src/laybasic/laybasic/layCellSelectionForm.cc +++ b/src/laybasic/laybasic/layCellSelectionForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layCellSelectionForm.h b/src/laybasic/laybasic/layCellSelectionForm.h index 65f4c4977..ae1b8e789 100644 --- a/src/laybasic/laybasic/layCellSelectionForm.h +++ b/src/laybasic/laybasic/layCellSelectionForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layCellTreeModel.cc b/src/laybasic/laybasic/layCellTreeModel.cc index 2ecb560ab..09ab23d67 100644 --- a/src/laybasic/laybasic/layCellTreeModel.cc +++ b/src/laybasic/laybasic/layCellTreeModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layCellTreeModel.h b/src/laybasic/laybasic/layCellTreeModel.h index e9c0afa9f..721df481b 100644 --- a/src/laybasic/laybasic/layCellTreeModel.h +++ b/src/laybasic/laybasic/layCellTreeModel.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layCellView.cc b/src/laybasic/laybasic/layCellView.cc index b21627566..6c44e29de 100644 --- a/src/laybasic/laybasic/layCellView.cc +++ b/src/laybasic/laybasic/layCellView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layCellView.h b/src/laybasic/laybasic/layCellView.h index 998b798da..9da62a3fc 100644 --- a/src/laybasic/laybasic/layCellView.h +++ b/src/laybasic/laybasic/layCellView.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layColorPalette.cc b/src/laybasic/laybasic/layColorPalette.cc index 3330dbe89..a24f8b1e6 100644 --- a/src/laybasic/laybasic/layColorPalette.cc +++ b/src/laybasic/laybasic/layColorPalette.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layColorPalette.h b/src/laybasic/laybasic/layColorPalette.h index 5089727ce..642774b28 100644 --- a/src/laybasic/laybasic/layColorPalette.h +++ b/src/laybasic/laybasic/layColorPalette.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layConfigurationDialog.cc b/src/laybasic/laybasic/layConfigurationDialog.cc index ca05932b9..34b8187c6 100644 --- a/src/laybasic/laybasic/layConfigurationDialog.cc +++ b/src/laybasic/laybasic/layConfigurationDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layConfigurationDialog.h b/src/laybasic/laybasic/layConfigurationDialog.h index 3607f33a8..a892f4799 100644 --- a/src/laybasic/laybasic/layConfigurationDialog.h +++ b/src/laybasic/laybasic/layConfigurationDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layConverters.cc b/src/laybasic/laybasic/layConverters.cc index aa6ece504..7b7c512ab 100644 --- a/src/laybasic/laybasic/layConverters.cc +++ b/src/laybasic/laybasic/layConverters.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layConverters.h b/src/laybasic/laybasic/layConverters.h index f164b899e..c5b6f0f3f 100644 --- a/src/laybasic/laybasic/layConverters.h +++ b/src/laybasic/laybasic/layConverters.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layCursor.cc b/src/laybasic/laybasic/layCursor.cc index 31277deb0..f1bc05167 100644 --- a/src/laybasic/laybasic/layCursor.cc +++ b/src/laybasic/laybasic/layCursor.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layCursor.h b/src/laybasic/laybasic/layCursor.h index a2050ca03..9826a5f08 100644 --- a/src/laybasic/laybasic/layCursor.h +++ b/src/laybasic/laybasic/layCursor.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layDialogs.cc b/src/laybasic/laybasic/layDialogs.cc index 31fb50d10..41ca22a0f 100644 --- a/src/laybasic/laybasic/layDialogs.cc +++ b/src/laybasic/laybasic/layDialogs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layDialogs.h b/src/laybasic/laybasic/layDialogs.h index 97a86c5db..243923fa7 100644 --- a/src/laybasic/laybasic/layDialogs.h +++ b/src/laybasic/laybasic/layDialogs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layDisplayState.cc b/src/laybasic/laybasic/layDisplayState.cc index a91aed1c1..400fdd67e 100644 --- a/src/laybasic/laybasic/layDisplayState.cc +++ b/src/laybasic/laybasic/layDisplayState.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layDisplayState.h b/src/laybasic/laybasic/layDisplayState.h index 11bdca4b7..7aef062e0 100644 --- a/src/laybasic/laybasic/layDisplayState.h +++ b/src/laybasic/laybasic/layDisplayState.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layDitherPattern.cc b/src/laybasic/laybasic/layDitherPattern.cc index ad95f8ea4..f4afad80b 100644 --- a/src/laybasic/laybasic/layDitherPattern.cc +++ b/src/laybasic/laybasic/layDitherPattern.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layDitherPattern.h b/src/laybasic/laybasic/layDitherPattern.h index 95aedda5f..c3010ab48 100644 --- a/src/laybasic/laybasic/layDitherPattern.h +++ b/src/laybasic/laybasic/layDitherPattern.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layDrawing.cc b/src/laybasic/laybasic/layDrawing.cc index 6f82ff236..58dbe7726 100644 --- a/src/laybasic/laybasic/layDrawing.cc +++ b/src/laybasic/laybasic/layDrawing.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layDrawing.h b/src/laybasic/laybasic/layDrawing.h index b72107338..8991d175a 100644 --- a/src/laybasic/laybasic/layDrawing.h +++ b/src/laybasic/laybasic/layDrawing.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layEditLineStyleWidget.cc b/src/laybasic/laybasic/layEditLineStyleWidget.cc index 301cf3851..fed39450e 100644 --- a/src/laybasic/laybasic/layEditLineStyleWidget.cc +++ b/src/laybasic/laybasic/layEditLineStyleWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layEditLineStyleWidget.h b/src/laybasic/laybasic/layEditLineStyleWidget.h index 1520a8bb3..1a632f4c0 100644 --- a/src/laybasic/laybasic/layEditLineStyleWidget.h +++ b/src/laybasic/laybasic/layEditLineStyleWidget.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layEditLineStylesForm.cc b/src/laybasic/laybasic/layEditLineStylesForm.cc index 95346ac63..99e3b465a 100644 --- a/src/laybasic/laybasic/layEditLineStylesForm.cc +++ b/src/laybasic/laybasic/layEditLineStylesForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layEditLineStylesForm.h b/src/laybasic/laybasic/layEditLineStylesForm.h index fa2fb6fdf..402c3c6ae 100644 --- a/src/laybasic/laybasic/layEditLineStylesForm.h +++ b/src/laybasic/laybasic/layEditLineStylesForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layEditStippleWidget.cc b/src/laybasic/laybasic/layEditStippleWidget.cc index b96da73e7..3726db60d 100644 --- a/src/laybasic/laybasic/layEditStippleWidget.cc +++ b/src/laybasic/laybasic/layEditStippleWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layEditStippleWidget.h b/src/laybasic/laybasic/layEditStippleWidget.h index 089a81501..536d19c14 100644 --- a/src/laybasic/laybasic/layEditStippleWidget.h +++ b/src/laybasic/laybasic/layEditStippleWidget.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layEditStipplesForm.cc b/src/laybasic/laybasic/layEditStipplesForm.cc index 6e067cd90..d9011a0b1 100644 --- a/src/laybasic/laybasic/layEditStipplesForm.cc +++ b/src/laybasic/laybasic/layEditStipplesForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layEditStipplesForm.h b/src/laybasic/laybasic/layEditStipplesForm.h index 39e9af9f0..34ff3b421 100644 --- a/src/laybasic/laybasic/layEditStipplesForm.h +++ b/src/laybasic/laybasic/layEditStipplesForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layEditable.cc b/src/laybasic/laybasic/layEditable.cc index 0a4044d33..fedceef0b 100644 --- a/src/laybasic/laybasic/layEditable.cc +++ b/src/laybasic/laybasic/layEditable.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layEditable.h b/src/laybasic/laybasic/layEditable.h index 5ac25a0f6..66b8d1738 100644 --- a/src/laybasic/laybasic/layEditable.h +++ b/src/laybasic/laybasic/layEditable.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layFileDialog.cc b/src/laybasic/laybasic/layFileDialog.cc index f8ca3c1fa..1eef62987 100644 --- a/src/laybasic/laybasic/layFileDialog.cc +++ b/src/laybasic/laybasic/layFileDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layFileDialog.h b/src/laybasic/laybasic/layFileDialog.h index 61eb239c0..899a82d06 100644 --- a/src/laybasic/laybasic/layFileDialog.h +++ b/src/laybasic/laybasic/layFileDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index d21535a9f..e5f8de362 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h index 6b5601666..382b4c841 100644 --- a/src/laybasic/laybasic/layFinder.h +++ b/src/laybasic/laybasic/layFinder.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layFixedFont.cc b/src/laybasic/laybasic/layFixedFont.cc index 0e1461dc3..4c553eaeb 100644 --- a/src/laybasic/laybasic/layFixedFont.cc +++ b/src/laybasic/laybasic/layFixedFont.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layGridNet.cc b/src/laybasic/laybasic/layGridNet.cc index be3b4a7e4..97e76d75f 100644 --- a/src/laybasic/laybasic/layGridNet.cc +++ b/src/laybasic/laybasic/layGridNet.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layGridNet.h b/src/laybasic/laybasic/layGridNet.h index 6e40889bf..489ec99ec 100644 --- a/src/laybasic/laybasic/layGridNet.h +++ b/src/laybasic/laybasic/layGridNet.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layHierarchyControlPanel.cc b/src/laybasic/laybasic/layHierarchyControlPanel.cc index e42b6b77e..f8e391e7e 100644 --- a/src/laybasic/laybasic/layHierarchyControlPanel.cc +++ b/src/laybasic/laybasic/layHierarchyControlPanel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layHierarchyControlPanel.h b/src/laybasic/laybasic/layHierarchyControlPanel.h index 0e8cb75a8..8520d562c 100644 --- a/src/laybasic/laybasic/layHierarchyControlPanel.h +++ b/src/laybasic/laybasic/layHierarchyControlPanel.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayerControlPanel.cc b/src/laybasic/laybasic/layLayerControlPanel.cc index 4ee34939d..1767f9e4c 100644 --- a/src/laybasic/laybasic/layLayerControlPanel.cc +++ b/src/laybasic/laybasic/layLayerControlPanel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayerControlPanel.h b/src/laybasic/laybasic/layLayerControlPanel.h index e334a20cd..c12b98e90 100644 --- a/src/laybasic/laybasic/layLayerControlPanel.h +++ b/src/laybasic/laybasic/layLayerControlPanel.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayerMappingWidget.cc b/src/laybasic/laybasic/layLayerMappingWidget.cc index ad07e4718..520af55aa 100644 --- a/src/laybasic/laybasic/layLayerMappingWidget.cc +++ b/src/laybasic/laybasic/layLayerMappingWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayerMappingWidget.h b/src/laybasic/laybasic/layLayerMappingWidget.h index 5142492ab..a99f99903 100644 --- a/src/laybasic/laybasic/layLayerMappingWidget.h +++ b/src/laybasic/laybasic/layLayerMappingWidget.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayerProperties.cc b/src/laybasic/laybasic/layLayerProperties.cc index 628e638ac..dc91e6b62 100644 --- a/src/laybasic/laybasic/layLayerProperties.cc +++ b/src/laybasic/laybasic/layLayerProperties.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayerProperties.h b/src/laybasic/laybasic/layLayerProperties.h index 503f23bf8..98e195771 100644 --- a/src/laybasic/laybasic/layLayerProperties.h +++ b/src/laybasic/laybasic/layLayerProperties.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayerToolbox.cc b/src/laybasic/laybasic/layLayerToolbox.cc index b1e303714..7a527989a 100644 --- a/src/laybasic/laybasic/layLayerToolbox.cc +++ b/src/laybasic/laybasic/layLayerToolbox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayerToolbox.h b/src/laybasic/laybasic/layLayerToolbox.h index 6f51327ac..7e67357e3 100644 --- a/src/laybasic/laybasic/layLayerToolbox.h +++ b/src/laybasic/laybasic/layLayerToolbox.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayerTreeModel.cc b/src/laybasic/laybasic/layLayerTreeModel.cc index d53b1ccf0..486c376db 100644 --- a/src/laybasic/laybasic/layLayerTreeModel.cc +++ b/src/laybasic/laybasic/layLayerTreeModel.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayerTreeModel.h b/src/laybasic/laybasic/layLayerTreeModel.h index ecb856fca..aedba6ca0 100644 --- a/src/laybasic/laybasic/layLayerTreeModel.h +++ b/src/laybasic/laybasic/layLayerTreeModel.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayoutCanvas.cc b/src/laybasic/laybasic/layLayoutCanvas.cc index ae6fa4379..6c86e300c 100644 --- a/src/laybasic/laybasic/layLayoutCanvas.cc +++ b/src/laybasic/laybasic/layLayoutCanvas.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayoutCanvas.h b/src/laybasic/laybasic/layLayoutCanvas.h index 35eb047c8..c8dc4d6f0 100644 --- a/src/laybasic/laybasic/layLayoutCanvas.h +++ b/src/laybasic/laybasic/layLayoutCanvas.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayoutPropertiesForm.cc b/src/laybasic/laybasic/layLayoutPropertiesForm.cc index 65319fb16..159644ee3 100644 --- a/src/laybasic/laybasic/layLayoutPropertiesForm.cc +++ b/src/laybasic/laybasic/layLayoutPropertiesForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayoutPropertiesForm.h b/src/laybasic/laybasic/layLayoutPropertiesForm.h index 64a14a36d..69aae6b59 100644 --- a/src/laybasic/laybasic/layLayoutPropertiesForm.h +++ b/src/laybasic/laybasic/layLayoutPropertiesForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 52d3deb34..1612bf292 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index 4d3e8b383..afffc8d7c 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayoutViewConfigPages.cc b/src/laybasic/laybasic/layLayoutViewConfigPages.cc index dd31d6856..94187cae1 100644 --- a/src/laybasic/laybasic/layLayoutViewConfigPages.cc +++ b/src/laybasic/laybasic/layLayoutViewConfigPages.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLayoutViewConfigPages.h b/src/laybasic/laybasic/layLayoutViewConfigPages.h index 9c3ddba2d..87e85e8cb 100644 --- a/src/laybasic/laybasic/layLayoutViewConfigPages.h +++ b/src/laybasic/laybasic/layLayoutViewConfigPages.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLineStylePalette.cc b/src/laybasic/laybasic/layLineStylePalette.cc index a73c65780..8c3cea224 100644 --- a/src/laybasic/laybasic/layLineStylePalette.cc +++ b/src/laybasic/laybasic/layLineStylePalette.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLineStylePalette.h b/src/laybasic/laybasic/layLineStylePalette.h index 96e0e27ed..5712adb9a 100644 --- a/src/laybasic/laybasic/layLineStylePalette.h +++ b/src/laybasic/laybasic/layLineStylePalette.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLineStyles.cc b/src/laybasic/laybasic/layLineStyles.cc index f43287876..70db633bc 100644 --- a/src/laybasic/laybasic/layLineStyles.cc +++ b/src/laybasic/laybasic/layLineStyles.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLineStyles.h b/src/laybasic/laybasic/layLineStyles.h index 98c2ac61d..4a2197306 100644 --- a/src/laybasic/laybasic/layLineStyles.h +++ b/src/laybasic/laybasic/layLineStyles.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLoadLayoutOptionsDialog.cc b/src/laybasic/laybasic/layLoadLayoutOptionsDialog.cc index 9fc9d487f..631ed3fdd 100644 --- a/src/laybasic/laybasic/layLoadLayoutOptionsDialog.cc +++ b/src/laybasic/laybasic/layLoadLayoutOptionsDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layLoadLayoutOptionsDialog.h b/src/laybasic/laybasic/layLoadLayoutOptionsDialog.h index 0a7b78b9d..1f09eef47 100644 --- a/src/laybasic/laybasic/layLoadLayoutOptionsDialog.h +++ b/src/laybasic/laybasic/layLoadLayoutOptionsDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layMarker.cc b/src/laybasic/laybasic/layMarker.cc index 45a3d1554..6a3e5a9a8 100644 --- a/src/laybasic/laybasic/layMarker.cc +++ b/src/laybasic/laybasic/layMarker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layMarker.h b/src/laybasic/laybasic/layMarker.h index c3ac07b44..f9e4f4bf7 100644 --- a/src/laybasic/laybasic/layMarker.h +++ b/src/laybasic/laybasic/layMarker.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layMouseTracker.cc b/src/laybasic/laybasic/layMouseTracker.cc index d4b893d3e..06b883a41 100644 --- a/src/laybasic/laybasic/layMouseTracker.cc +++ b/src/laybasic/laybasic/layMouseTracker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layMouseTracker.h b/src/laybasic/laybasic/layMouseTracker.h index 40edcb0dd..e088a5bc6 100644 --- a/src/laybasic/laybasic/layMouseTracker.h +++ b/src/laybasic/laybasic/layMouseTracker.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 1fb9b61ee..deb9d5e56 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index 2c375e82b..84ff6c7dd 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layObjectInstPath.cc b/src/laybasic/laybasic/layObjectInstPath.cc index fa013790c..487114788 100644 --- a/src/laybasic/laybasic/layObjectInstPath.cc +++ b/src/laybasic/laybasic/layObjectInstPath.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layObjectInstPath.h b/src/laybasic/laybasic/layObjectInstPath.h index 56ace6ec5..92c356abc 100644 --- a/src/laybasic/laybasic/layObjectInstPath.h +++ b/src/laybasic/laybasic/layObjectInstPath.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layParsedLayerSource.cc b/src/laybasic/laybasic/layParsedLayerSource.cc index 500a773ea..35246d649 100644 --- a/src/laybasic/laybasic/layParsedLayerSource.cc +++ b/src/laybasic/laybasic/layParsedLayerSource.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layParsedLayerSource.h b/src/laybasic/laybasic/layParsedLayerSource.h index 371526bdf..9c6950f2e 100644 --- a/src/laybasic/laybasic/layParsedLayerSource.h +++ b/src/laybasic/laybasic/layParsedLayerSource.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layPlugin.cc b/src/laybasic/laybasic/layPlugin.cc index bb700cdb9..955ced3aa 100644 --- a/src/laybasic/laybasic/layPlugin.cc +++ b/src/laybasic/laybasic/layPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layPlugin.h b/src/laybasic/laybasic/layPlugin.h index 49b79a91f..709aa1227 100644 --- a/src/laybasic/laybasic/layPlugin.h +++ b/src/laybasic/laybasic/layPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layProperties.cc b/src/laybasic/laybasic/layProperties.cc index 00ade6263..7b381dd7e 100644 --- a/src/laybasic/laybasic/layProperties.cc +++ b/src/laybasic/laybasic/layProperties.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layProperties.h b/src/laybasic/laybasic/layProperties.h index 16adc4720..f466710d7 100644 --- a/src/laybasic/laybasic/layProperties.h +++ b/src/laybasic/laybasic/layProperties.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layPropertiesDialog.cc b/src/laybasic/laybasic/layPropertiesDialog.cc index 1e4b80f36..f668b285c 100644 --- a/src/laybasic/laybasic/layPropertiesDialog.cc +++ b/src/laybasic/laybasic/layPropertiesDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layPropertiesDialog.h b/src/laybasic/laybasic/layPropertiesDialog.h index d852d660b..342b88efa 100644 --- a/src/laybasic/laybasic/layPropertiesDialog.h +++ b/src/laybasic/laybasic/layPropertiesDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layQtTools.cc b/src/laybasic/laybasic/layQtTools.cc index f7642e8d7..220f2c8d9 100644 --- a/src/laybasic/laybasic/layQtTools.cc +++ b/src/laybasic/laybasic/layQtTools.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layQtTools.h b/src/laybasic/laybasic/layQtTools.h index dd72ba301..5d6465533 100644 --- a/src/laybasic/laybasic/layQtTools.h +++ b/src/laybasic/laybasic/layQtTools.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRedrawLayerInfo.cc b/src/laybasic/laybasic/layRedrawLayerInfo.cc index 3f991c96d..7ce369a31 100644 --- a/src/laybasic/laybasic/layRedrawLayerInfo.cc +++ b/src/laybasic/laybasic/layRedrawLayerInfo.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRedrawLayerInfo.h b/src/laybasic/laybasic/layRedrawLayerInfo.h index 60865b241..4f546d500 100644 --- a/src/laybasic/laybasic/layRedrawLayerInfo.h +++ b/src/laybasic/laybasic/layRedrawLayerInfo.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRedrawThread.cc b/src/laybasic/laybasic/layRedrawThread.cc index 757692ada..324786298 100644 --- a/src/laybasic/laybasic/layRedrawThread.cc +++ b/src/laybasic/laybasic/layRedrawThread.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRedrawThread.h b/src/laybasic/laybasic/layRedrawThread.h index 5880e3dc8..b5ac65b14 100644 --- a/src/laybasic/laybasic/layRedrawThread.h +++ b/src/laybasic/laybasic/layRedrawThread.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRedrawThreadCanvas.cc b/src/laybasic/laybasic/layRedrawThreadCanvas.cc index a5c2a1a9c..0016d21a9 100644 --- a/src/laybasic/laybasic/layRedrawThreadCanvas.cc +++ b/src/laybasic/laybasic/layRedrawThreadCanvas.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRedrawThreadCanvas.h b/src/laybasic/laybasic/layRedrawThreadCanvas.h index 0c14a116e..eb43a189e 100644 --- a/src/laybasic/laybasic/layRedrawThreadCanvas.h +++ b/src/laybasic/laybasic/layRedrawThreadCanvas.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index daabbdac3..c71f31495 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.h b/src/laybasic/laybasic/layRedrawThreadWorker.h index ea2a90a11..9976fa433 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.h +++ b/src/laybasic/laybasic/layRedrawThreadWorker.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRenderer.cc b/src/laybasic/laybasic/layRenderer.cc index 2b0a46b54..c96d11a1e 100644 --- a/src/laybasic/laybasic/layRenderer.cc +++ b/src/laybasic/laybasic/layRenderer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRenderer.h b/src/laybasic/laybasic/layRenderer.h index 1a81c71b4..42a85b42e 100644 --- a/src/laybasic/laybasic/layRenderer.h +++ b/src/laybasic/laybasic/layRenderer.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRubberBox.cc b/src/laybasic/laybasic/layRubberBox.cc index bbffac8c7..9ff43d277 100644 --- a/src/laybasic/laybasic/layRubberBox.cc +++ b/src/laybasic/laybasic/layRubberBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layRubberBox.h b/src/laybasic/laybasic/layRubberBox.h index ada800937..cf7e531a8 100644 --- a/src/laybasic/laybasic/layRubberBox.h +++ b/src/laybasic/laybasic/layRubberBox.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laySaveLayoutOptionsDialog.cc b/src/laybasic/laybasic/laySaveLayoutOptionsDialog.cc index 0876a3549..dd8b34840 100644 --- a/src/laybasic/laybasic/laySaveLayoutOptionsDialog.cc +++ b/src/laybasic/laybasic/laySaveLayoutOptionsDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laySaveLayoutOptionsDialog.h b/src/laybasic/laybasic/laySaveLayoutOptionsDialog.h index 64d69ce34..50ac37e2f 100644 --- a/src/laybasic/laybasic/laySaveLayoutOptionsDialog.h +++ b/src/laybasic/laybasic/laySaveLayoutOptionsDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laySelectLineStyleForm.cc b/src/laybasic/laybasic/laySelectLineStyleForm.cc index af9a21408..4fc01529c 100644 --- a/src/laybasic/laybasic/laySelectLineStyleForm.cc +++ b/src/laybasic/laybasic/laySelectLineStyleForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laySelectLineStyleForm.h b/src/laybasic/laybasic/laySelectLineStyleForm.h index e7183cad6..e2a88e5d2 100644 --- a/src/laybasic/laybasic/laySelectLineStyleForm.h +++ b/src/laybasic/laybasic/laySelectLineStyleForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laySelectStippleForm.cc b/src/laybasic/laybasic/laySelectStippleForm.cc index 24af22a7c..1f472a964 100644 --- a/src/laybasic/laybasic/laySelectStippleForm.cc +++ b/src/laybasic/laybasic/laySelectStippleForm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laySelectStippleForm.h b/src/laybasic/laybasic/laySelectStippleForm.h index 6ca495253..4b0da2f29 100644 --- a/src/laybasic/laybasic/laySelectStippleForm.h +++ b/src/laybasic/laybasic/laySelectStippleForm.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laySelector.cc b/src/laybasic/laybasic/laySelector.cc index 91f5e32f3..4f07bbd36 100644 --- a/src/laybasic/laybasic/laySelector.cc +++ b/src/laybasic/laybasic/laySelector.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laySelector.h b/src/laybasic/laybasic/laySelector.h index d6795e93f..1bbb2dcd2 100644 --- a/src/laybasic/laybasic/laySelector.h +++ b/src/laybasic/laybasic/laySelector.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laySnap.cc b/src/laybasic/laybasic/laySnap.cc index 193a1cb67..a524ec607 100644 --- a/src/laybasic/laybasic/laySnap.cc +++ b/src/laybasic/laybasic/laySnap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laySnap.h b/src/laybasic/laybasic/laySnap.h index d906489b3..9e7248446 100644 --- a/src/laybasic/laybasic/laySnap.h +++ b/src/laybasic/laybasic/laySnap.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layStipplePalette.cc b/src/laybasic/laybasic/layStipplePalette.cc index af0fb5c7e..3eda7d93b 100644 --- a/src/laybasic/laybasic/layStipplePalette.cc +++ b/src/laybasic/laybasic/layStipplePalette.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layStipplePalette.h b/src/laybasic/laybasic/layStipplePalette.h index 38e246b68..2f82b5a28 100644 --- a/src/laybasic/laybasic/layStipplePalette.h +++ b/src/laybasic/laybasic/layStipplePalette.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layStream.cc b/src/laybasic/laybasic/layStream.cc index 6e3bf3abb..d8a591a28 100644 --- a/src/laybasic/laybasic/layStream.cc +++ b/src/laybasic/laybasic/layStream.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layStream.h b/src/laybasic/laybasic/layStream.h index 7dbb903ce..53b929d58 100644 --- a/src/laybasic/laybasic/layStream.h +++ b/src/laybasic/laybasic/layStream.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layTechnology.cc b/src/laybasic/laybasic/layTechnology.cc index b7a8783e7..9656d290e 100644 --- a/src/laybasic/laybasic/layTechnology.cc +++ b/src/laybasic/laybasic/layTechnology.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layTechnology.h b/src/laybasic/laybasic/layTechnology.h index e9aedb21d..011026cdc 100644 --- a/src/laybasic/laybasic/layTechnology.h +++ b/src/laybasic/laybasic/layTechnology.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layTipDialog.cc b/src/laybasic/laybasic/layTipDialog.cc index c5aa71858..75d678566 100644 --- a/src/laybasic/laybasic/layTipDialog.cc +++ b/src/laybasic/laybasic/layTipDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layTipDialog.h b/src/laybasic/laybasic/layTipDialog.h index 9cfd99373..4aee2d576 100644 --- a/src/laybasic/laybasic/layTipDialog.h +++ b/src/laybasic/laybasic/layTipDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc index 9e52c8acb..affdb81d2 100644 --- a/src/laybasic/laybasic/layViewObject.cc +++ b/src/laybasic/laybasic/layViewObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index 6a274aab8..432eb585d 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layViewOp.cc b/src/laybasic/laybasic/layViewOp.cc index f4f03bd2a..cb8fced04 100644 --- a/src/laybasic/laybasic/layViewOp.cc +++ b/src/laybasic/laybasic/layViewOp.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layViewOp.h b/src/laybasic/laybasic/layViewOp.h index 82f25990b..55ad13481 100644 --- a/src/laybasic/laybasic/layViewOp.h +++ b/src/laybasic/laybasic/layViewOp.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layViewport.cc b/src/laybasic/laybasic/layViewport.cc index db5441c08..e214ba54b 100644 --- a/src/laybasic/laybasic/layViewport.cc +++ b/src/laybasic/laybasic/layViewport.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layViewport.h b/src/laybasic/laybasic/layViewport.h index bfa79e8d5..19da35487 100644 --- a/src/laybasic/laybasic/layViewport.h +++ b/src/laybasic/laybasic/layViewport.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layWidgets.cc b/src/laybasic/laybasic/layWidgets.cc index 96f1ef629..529bdaa14 100644 --- a/src/laybasic/laybasic/layWidgets.cc +++ b/src/laybasic/laybasic/layWidgets.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layWidgets.h b/src/laybasic/laybasic/layWidgets.h index 5cd62f76c..a05e1846e 100644 --- a/src/laybasic/laybasic/layWidgets.h +++ b/src/laybasic/laybasic/layWidgets.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layZoomBox.cc b/src/laybasic/laybasic/layZoomBox.cc index 63d1e7092..5523b09be 100644 --- a/src/laybasic/laybasic/layZoomBox.cc +++ b/src/laybasic/laybasic/layZoomBox.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/layZoomBox.h b/src/laybasic/laybasic/layZoomBox.h index 824c93a8c..f19b6b46e 100644 --- a/src/laybasic/laybasic/layZoomBox.h +++ b/src/laybasic/laybasic/layZoomBox.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laybasicCommon.h b/src/laybasic/laybasic/laybasicCommon.h index cb861f48f..2dfe658a9 100644 --- a/src/laybasic/laybasic/laybasicCommon.h +++ b/src/laybasic/laybasic/laybasicCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index 6f8aafaed..58788e56e 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/rdbInfoWidget.cc b/src/laybasic/laybasic/rdbInfoWidget.cc index 9af26fc68..4dc758f39 100644 --- a/src/laybasic/laybasic/rdbInfoWidget.cc +++ b/src/laybasic/laybasic/rdbInfoWidget.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/rdbInfoWidget.h b/src/laybasic/laybasic/rdbInfoWidget.h index b4ffcd338..ca374a140 100644 --- a/src/laybasic/laybasic/rdbInfoWidget.h +++ b/src/laybasic/laybasic/rdbInfoWidget.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/rdbMarkerBrowser.cc b/src/laybasic/laybasic/rdbMarkerBrowser.cc index 5c122c5d6..e4d15baca 100644 --- a/src/laybasic/laybasic/rdbMarkerBrowser.cc +++ b/src/laybasic/laybasic/rdbMarkerBrowser.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/rdbMarkerBrowser.h b/src/laybasic/laybasic/rdbMarkerBrowser.h index da803bbfe..b8a47c307 100644 --- a/src/laybasic/laybasic/rdbMarkerBrowser.h +++ b/src/laybasic/laybasic/rdbMarkerBrowser.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/rdbMarkerBrowserDialog.cc b/src/laybasic/laybasic/rdbMarkerBrowserDialog.cc index 401ff756a..0d007b519 100644 --- a/src/laybasic/laybasic/rdbMarkerBrowserDialog.cc +++ b/src/laybasic/laybasic/rdbMarkerBrowserDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/rdbMarkerBrowserDialog.h b/src/laybasic/laybasic/rdbMarkerBrowserDialog.h index e276843e7..8e8f31e85 100644 --- a/src/laybasic/laybasic/rdbMarkerBrowserDialog.h +++ b/src/laybasic/laybasic/rdbMarkerBrowserDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/rdbMarkerBrowserPage.cc b/src/laybasic/laybasic/rdbMarkerBrowserPage.cc index 303455ee7..1948b6f95 100644 --- a/src/laybasic/laybasic/rdbMarkerBrowserPage.cc +++ b/src/laybasic/laybasic/rdbMarkerBrowserPage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/laybasic/rdbMarkerBrowserPage.h b/src/laybasic/laybasic/rdbMarkerBrowserPage.h index 75cfa2bce..982395720 100644 --- a/src/laybasic/laybasic/rdbMarkerBrowserPage.h +++ b/src/laybasic/laybasic/rdbMarkerBrowserPage.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/unit_tests/layAbstractMenu.cc b/src/laybasic/unit_tests/layAbstractMenu.cc index 46bd3b3ff..a220bfca3 100644 --- a/src/laybasic/unit_tests/layAbstractMenu.cc +++ b/src/laybasic/unit_tests/layAbstractMenu.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/unit_tests/layAnnotationShapes.cc b/src/laybasic/unit_tests/layAnnotationShapes.cc index 53c8b87b2..cc254d4b7 100644 --- a/src/laybasic/unit_tests/layAnnotationShapes.cc +++ b/src/laybasic/unit_tests/layAnnotationShapes.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/unit_tests/layBitmap.cc b/src/laybasic/unit_tests/layBitmap.cc index 9af165d95..8b7472f36 100644 --- a/src/laybasic/unit_tests/layBitmap.cc +++ b/src/laybasic/unit_tests/layBitmap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/unit_tests/layBitmapsToImage.cc b/src/laybasic/unit_tests/layBitmapsToImage.cc index 82b3a466c..02e5bd88f 100644 --- a/src/laybasic/unit_tests/layBitmapsToImage.cc +++ b/src/laybasic/unit_tests/layBitmapsToImage.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/unit_tests/layLayerProperties.cc b/src/laybasic/unit_tests/layLayerProperties.cc index f43fd7fa5..832b2ee99 100644 --- a/src/laybasic/unit_tests/layLayerProperties.cc +++ b/src/laybasic/unit_tests/layLayerProperties.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/unit_tests/layParsedLayerSource.cc b/src/laybasic/unit_tests/layParsedLayerSource.cc index 2887a656a..103dba0d1 100644 --- a/src/laybasic/unit_tests/layParsedLayerSource.cc +++ b/src/laybasic/unit_tests/layParsedLayerSource.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/unit_tests/layRenderer.cc b/src/laybasic/unit_tests/layRenderer.cc index e172a094c..a3b9ac876 100644 --- a/src/laybasic/unit_tests/layRenderer.cc +++ b/src/laybasic/unit_tests/layRenderer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/laybasic/unit_tests/laySnap.cc b/src/laybasic/unit_tests/laySnap.cc index 501f9313c..8e299d9c8 100644 --- a/src/laybasic/unit_tests/laySnap.cc +++ b/src/laybasic/unit_tests/laySnap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasic.cc b/src/lib/lib/libBasic.cc index 6230cd9dd..f7492bf08 100644 --- a/src/lib/lib/libBasic.cc +++ b/src/lib/lib/libBasic.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicArc.cc b/src/lib/lib/libBasicArc.cc index 47b8a3d00..27112d4d3 100644 --- a/src/lib/lib/libBasicArc.cc +++ b/src/lib/lib/libBasicArc.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicArc.h b/src/lib/lib/libBasicArc.h index ee8c5da88..3f6ca9882 100644 --- a/src/lib/lib/libBasicArc.h +++ b/src/lib/lib/libBasicArc.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicCircle.cc b/src/lib/lib/libBasicCircle.cc index 1247a08ed..bcc29411c 100644 --- a/src/lib/lib/libBasicCircle.cc +++ b/src/lib/lib/libBasicCircle.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicCircle.h b/src/lib/lib/libBasicCircle.h index 9aac3ac92..fa9fb1734 100644 --- a/src/lib/lib/libBasicCircle.h +++ b/src/lib/lib/libBasicCircle.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicDonut.cc b/src/lib/lib/libBasicDonut.cc index b17dc9e09..9b1a53ad2 100644 --- a/src/lib/lib/libBasicDonut.cc +++ b/src/lib/lib/libBasicDonut.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicDonut.h b/src/lib/lib/libBasicDonut.h index 13d18d7c2..7938adc6b 100644 --- a/src/lib/lib/libBasicDonut.h +++ b/src/lib/lib/libBasicDonut.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicEllipse.cc b/src/lib/lib/libBasicEllipse.cc index 558c5882a..1d213452e 100644 --- a/src/lib/lib/libBasicEllipse.cc +++ b/src/lib/lib/libBasicEllipse.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicEllipse.h b/src/lib/lib/libBasicEllipse.h index a331c36f3..66552bb1f 100644 --- a/src/lib/lib/libBasicEllipse.h +++ b/src/lib/lib/libBasicEllipse.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicPie.cc b/src/lib/lib/libBasicPie.cc index 6a22032fd..bd3bc6d12 100644 --- a/src/lib/lib/libBasicPie.cc +++ b/src/lib/lib/libBasicPie.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicPie.h b/src/lib/lib/libBasicPie.h index 07a913bc4..5fd45a7b0 100644 --- a/src/lib/lib/libBasicPie.h +++ b/src/lib/lib/libBasicPie.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicRoundPath.cc b/src/lib/lib/libBasicRoundPath.cc index eec00ec16..20931f27b 100644 --- a/src/lib/lib/libBasicRoundPath.cc +++ b/src/lib/lib/libBasicRoundPath.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicRoundPath.h b/src/lib/lib/libBasicRoundPath.h index ba05c725e..7a84d2069 100644 --- a/src/lib/lib/libBasicRoundPath.h +++ b/src/lib/lib/libBasicRoundPath.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicRoundPolygon.cc b/src/lib/lib/libBasicRoundPolygon.cc index 41223676a..110679a1b 100644 --- a/src/lib/lib/libBasicRoundPolygon.cc +++ b/src/lib/lib/libBasicRoundPolygon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicRoundPolygon.h b/src/lib/lib/libBasicRoundPolygon.h index d15f5157c..be55e471e 100644 --- a/src/lib/lib/libBasicRoundPolygon.h +++ b/src/lib/lib/libBasicRoundPolygon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicStrokedPolygon.cc b/src/lib/lib/libBasicStrokedPolygon.cc index 72f44b6eb..fb910acbb 100644 --- a/src/lib/lib/libBasicStrokedPolygon.cc +++ b/src/lib/lib/libBasicStrokedPolygon.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicStrokedPolygon.h b/src/lib/lib/libBasicStrokedPolygon.h index 7f7c8fd79..7087e1368 100644 --- a/src/lib/lib/libBasicStrokedPolygon.h +++ b/src/lib/lib/libBasicStrokedPolygon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicText.cc b/src/lib/lib/libBasicText.cc index d1ecdc7e1..84d95a761 100644 --- a/src/lib/lib/libBasicText.cc +++ b/src/lib/lib/libBasicText.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libBasicText.h b/src/lib/lib/libBasicText.h index 94b2183f6..f5b1e722d 100644 --- a/src/lib/lib/libBasicText.h +++ b/src/lib/lib/libBasicText.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libCommon.h b/src/lib/lib/libCommon.h index be7631dea..6a839d46b 100644 --- a/src/lib/lib/libCommon.h +++ b/src/lib/lib/libCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libForceLink.cc b/src/lib/lib/libForceLink.cc index 0ce99454a..235ab6e06 100644 --- a/src/lib/lib/libForceLink.cc +++ b/src/lib/lib/libForceLink.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/lib/libForceLink.h b/src/lib/lib/libForceLink.h index 9db6e84c0..44617513f 100644 --- a/src/lib/lib/libForceLink.h +++ b/src/lib/lib/libForceLink.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lib/unit_tests/libBasicTests.cc b/src/lib/unit_tests/libBasicTests.cc index 249c74ccb..1b22f65b8 100644 --- a/src/lib/unit_tests/libBasicTests.cc +++ b/src/lib/unit_tests/libBasicTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lym/lym/gsiDeclLymMacro.cc b/src/lym/lym/gsiDeclLymMacro.cc index e1f4f5317..390c57e5c 100644 --- a/src/lym/lym/gsiDeclLymMacro.cc +++ b/src/lym/lym/gsiDeclLymMacro.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lym/lym/lymCommon.h b/src/lym/lym/lymCommon.h index 208966993..5071eabba 100644 --- a/src/lym/lym/lymCommon.h +++ b/src/lym/lym/lymCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc index b6fd483b4..1eb7b27e9 100644 --- a/src/lym/lym/lymMacro.cc +++ b/src/lym/lym/lymMacro.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lym/lym/lymMacro.h b/src/lym/lym/lymMacro.h index 46e813294..1e9b0a8a6 100644 --- a/src/lym/lym/lymMacro.h +++ b/src/lym/lym/lymMacro.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lym/lym/lymMacroInterpreter.cc b/src/lym/lym/lymMacroInterpreter.cc index 5bfc494a0..cf98e16ae 100644 --- a/src/lym/lym/lymMacroInterpreter.cc +++ b/src/lym/lym/lymMacroInterpreter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lym/lym/lymMacroInterpreter.h b/src/lym/lym/lymMacroInterpreter.h index cf4ace499..af3d9f166 100644 --- a/src/lym/lym/lymMacroInterpreter.h +++ b/src/lym/lym/lymMacroInterpreter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/lym/unit_tests/lymBasicTests.cc b/src/lym/unit_tests/lymBasicTests.cc index b230abb9a..bb163b737 100644 --- a/src/lym/unit_tests/lymBasicTests.cc +++ b/src/lym/unit_tests/lymBasicTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/common/dbPluginCommon.h b/src/plugins/common/dbPluginCommon.h index 40d068263..913db4c8a 100644 --- a/src/plugins/common/dbPluginCommon.h +++ b/src/plugins/common/dbPluginCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/common/layPluginCommon.h b/src/plugins/common/layPluginCommon.h index f055fddcf..5d061d9e2 100644 --- a/src/plugins/common/layPluginCommon.h +++ b/src/plugins/common/layPluginCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/db_plugin/dbCIF.cc b/src/plugins/streamers/cif/db_plugin/dbCIF.cc index 297a562ab..2218b33dc 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIF.cc +++ b/src/plugins/streamers/cif/db_plugin/dbCIF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/db_plugin/dbCIF.h b/src/plugins/streamers/cif/db_plugin/dbCIF.h index 7b560ba1f..b524d3a00 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIF.h +++ b/src/plugins/streamers/cif/db_plugin/dbCIF.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFFormat.h b/src/plugins/streamers/cif/db_plugin/dbCIFFormat.h index 9cf8a225f..44389c0f7 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIFFormat.h +++ b/src/plugins/streamers/cif/db_plugin/dbCIFFormat.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc b/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc index e07d10454..a7ef013bb 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc +++ b/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFReader.h b/src/plugins/streamers/cif/db_plugin/dbCIFReader.h index cea84eccc..fe560db8e 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIFReader.h +++ b/src/plugins/streamers/cif/db_plugin/dbCIFReader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc b/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc index 3bbf0468e..6422af9d8 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc +++ b/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFWriter.h b/src/plugins/streamers/cif/db_plugin/dbCIFWriter.h index b858eb1c5..d5ade32f8 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIFWriter.h +++ b/src/plugins/streamers/cif/db_plugin/dbCIFWriter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/db_plugin/gsiDeclDbCIF.cc b/src/plugins/streamers/cif/db_plugin/gsiDeclDbCIF.cc index fd3414092..ed41fb092 100644 --- a/src/plugins/streamers/cif/db_plugin/gsiDeclDbCIF.cc +++ b/src/plugins/streamers/cif/db_plugin/gsiDeclDbCIF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/lay_plugin/layCIFReaderPlugin.cc b/src/plugins/streamers/cif/lay_plugin/layCIFReaderPlugin.cc index 6e0c72158..336837dd5 100644 --- a/src/plugins/streamers/cif/lay_plugin/layCIFReaderPlugin.cc +++ b/src/plugins/streamers/cif/lay_plugin/layCIFReaderPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/lay_plugin/layCIFReaderPlugin.h b/src/plugins/streamers/cif/lay_plugin/layCIFReaderPlugin.h index b326a4379..1597f7ac0 100644 --- a/src/plugins/streamers/cif/lay_plugin/layCIFReaderPlugin.h +++ b/src/plugins/streamers/cif/lay_plugin/layCIFReaderPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/lay_plugin/layCIFWriterPlugin.cc b/src/plugins/streamers/cif/lay_plugin/layCIFWriterPlugin.cc index 4c334cd81..bbcc99a7b 100644 --- a/src/plugins/streamers/cif/lay_plugin/layCIFWriterPlugin.cc +++ b/src/plugins/streamers/cif/lay_plugin/layCIFWriterPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/lay_plugin/layCIFWriterPlugin.h b/src/plugins/streamers/cif/lay_plugin/layCIFWriterPlugin.h index 2bd24ee1c..f4aa6cb71 100644 --- a/src/plugins/streamers/cif/lay_plugin/layCIFWriterPlugin.h +++ b/src/plugins/streamers/cif/lay_plugin/layCIFWriterPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc b/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc index ea220f188..b032e14e6 100644 --- a/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc +++ b/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/common/lay_plugin/layCommonReaderPlugin.cc b/src/plugins/streamers/common/lay_plugin/layCommonReaderPlugin.cc index 6b02f55f7..422d4adca 100644 --- a/src/plugins/streamers/common/lay_plugin/layCommonReaderPlugin.cc +++ b/src/plugins/streamers/common/lay_plugin/layCommonReaderPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/common/lay_plugin/layCommonReaderPlugin.h b/src/plugins/streamers/common/lay_plugin/layCommonReaderPlugin.h index 3d61a3ebf..016008af6 100644 --- a/src/plugins/streamers/common/lay_plugin/layCommonReaderPlugin.h +++ b/src/plugins/streamers/common/lay_plugin/layCommonReaderPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXF.cc b/src/plugins/streamers/dxf/db_plugin/dbDXF.cc index adb1f0fdb..3b81a2be2 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXF.cc +++ b/src/plugins/streamers/dxf/db_plugin/dbDXF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXF.h b/src/plugins/streamers/dxf/db_plugin/dbDXF.h index 583c605af..11fcbe9a2 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXF.h +++ b/src/plugins/streamers/dxf/db_plugin/dbDXF.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFFormat.h b/src/plugins/streamers/dxf/db_plugin/dbDXFFormat.h index fd23120ff..a47c5263e 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFFormat.h +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFFormat.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc index 1792552a1..0a5c7c91b 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h index bef4d376d..81bb70468 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFWriter.cc b/src/plugins/streamers/dxf/db_plugin/dbDXFWriter.cc index 8ba1df8f8..5d8e34647 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFWriter.cc +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFWriter.h b/src/plugins/streamers/dxf/db_plugin/dbDXFWriter.h index ab39ed5f0..51d20621e 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFWriter.h +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFWriter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/db_plugin/gsiDeclDbDXF.cc b/src/plugins/streamers/dxf/db_plugin/gsiDeclDbDXF.cc index 5605ae011..d2bce9774 100755 --- a/src/plugins/streamers/dxf/db_plugin/gsiDeclDbDXF.cc +++ b/src/plugins/streamers/dxf/db_plugin/gsiDeclDbDXF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/lay_plugin/layDXFReaderPlugin.cc b/src/plugins/streamers/dxf/lay_plugin/layDXFReaderPlugin.cc index 3c81aee99..708e6275f 100644 --- a/src/plugins/streamers/dxf/lay_plugin/layDXFReaderPlugin.cc +++ b/src/plugins/streamers/dxf/lay_plugin/layDXFReaderPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/lay_plugin/layDXFReaderPlugin.h b/src/plugins/streamers/dxf/lay_plugin/layDXFReaderPlugin.h index b58a9b4d9..b655d15c2 100644 --- a/src/plugins/streamers/dxf/lay_plugin/layDXFReaderPlugin.h +++ b/src/plugins/streamers/dxf/lay_plugin/layDXFReaderPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/lay_plugin/layDXFWriterPlugin.cc b/src/plugins/streamers/dxf/lay_plugin/layDXFWriterPlugin.cc index bb6988b64..876da088a 100644 --- a/src/plugins/streamers/dxf/lay_plugin/layDXFWriterPlugin.cc +++ b/src/plugins/streamers/dxf/lay_plugin/layDXFWriterPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/lay_plugin/layDXFWriterPlugin.h b/src/plugins/streamers/dxf/lay_plugin/layDXFWriterPlugin.h index 8ef7dfdbf..3457b2fc3 100644 --- a/src/plugins/streamers/dxf/lay_plugin/layDXFWriterPlugin.h +++ b/src/plugins/streamers/dxf/lay_plugin/layDXFWriterPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/dxf/unit_tests/dbDXFReader.cc b/src/plugins/streamers/dxf/unit_tests/dbDXFReader.cc index e2c709851..de9f60665 100644 --- a/src/plugins/streamers/dxf/unit_tests/dbDXFReader.cc +++ b/src/plugins/streamers/dxf/unit_tests/dbDXFReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Converter.cc b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Converter.cc index ea3da98b4..3799f680a 100644 --- a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Converter.cc +++ b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Converter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Converter.h b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Converter.h index 699acaed5..43f4daca5 100644 --- a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Converter.h +++ b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Converter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Text.cc b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Text.cc index 8f3b1711a..e8ff09923 100644 --- a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Text.cc +++ b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2Text.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.cc b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.cc index e5b8cd51a..d66e09a5a 100644 --- a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.cc +++ b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h index 8cf00045c..21796ab8a 100644 --- a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h +++ b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextWriter.cc b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextWriter.cc index e1323dd26..974103954 100644 --- a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextWriter.cc +++ b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextWriter.h b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextWriter.h index 0ff572f01..2f1660595 100644 --- a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextWriter.h +++ b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextWriter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc index 5d2ddbc65..46077274f 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2.h index a37dffa5d..ecf0d9deb 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h index 9daf7056c..b03edeb52 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc index 9fb0a93bb..5b4b97bc5 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h index 1315c46f8..3e5be4b6e 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index f50827a60..4b59e0b5b 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h index fc7026179..cf931143f 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Writer.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2Writer.cc index 58ae565f6..b4cb6d24e 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Writer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Writer.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2Writer.h index 75675989f..8fc3b4d70 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Writer.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Writer.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 2881f9660..96f58c136 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h index 68dbcbde3..d0c5dd6cf 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc b/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc index 9d8329284..9ea126672 100644 --- a/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc +++ b/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/lay_plugin/layGDS2ReaderPlugin.cc b/src/plugins/streamers/gds2/lay_plugin/layGDS2ReaderPlugin.cc index feacb0fd4..d12fb6600 100644 --- a/src/plugins/streamers/gds2/lay_plugin/layGDS2ReaderPlugin.cc +++ b/src/plugins/streamers/gds2/lay_plugin/layGDS2ReaderPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/lay_plugin/layGDS2ReaderPlugin.h b/src/plugins/streamers/gds2/lay_plugin/layGDS2ReaderPlugin.h index cc54fad09..753bb18b7 100644 --- a/src/plugins/streamers/gds2/lay_plugin/layGDS2ReaderPlugin.h +++ b/src/plugins/streamers/gds2/lay_plugin/layGDS2ReaderPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc index ebb22aad7..e17e154d6 100644 --- a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc +++ b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.h b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.h index c381d8fc3..be1523e47 100644 --- a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.h +++ b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc index aed6d9931..d09f2a156 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc index b48066aad..8426c0866 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index 15638f065..f25f539d3 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h index 170e9a55a..ee40e1a73 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index 0e817f56f..72a604970 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index c61501cbb..753d6744b 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index fa756fc31..486c65497 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc index 639d52549..933d349db 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h index c1467cb5c..b5ce3a905 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc index 4e1964cb8..fd63fb303 100644 --- a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc +++ b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImport.cc b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImport.cc index 8871c4261..2e8356dac 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImport.cc +++ b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImport.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc index 371493203..bec2cfac5 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc +++ b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.h b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.h index d8c158896..f1e4bb7d9 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.h +++ b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFPlugin.cc index 00e2550ba..26be333e5 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImport.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImport.cc index 3da2eec81..2a3b6d95c 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImport.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImport.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASIS.cc b/src/plugins/streamers/oasis/db_plugin/dbOASIS.cc index 5fe5d45c4..8bfb6baff 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASIS.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASIS.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASIS.h b/src/plugins/streamers/oasis/db_plugin/dbOASIS.h index 7300296ee..1f773d862 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASIS.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASIS.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h index be9235159..8d3c2e886 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index a97d1e913..ebda040e4 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index 08d1d818c..89344c954 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index d9d9c5a17..980f661bc 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h index 8a4eb2eb3..da76aa4a2 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc b/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc index 5d6a78010..c242be070 100644 --- a/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc +++ b/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/lay_plugin/layOASISReaderPlugin.cc b/src/plugins/streamers/oasis/lay_plugin/layOASISReaderPlugin.cc index a1432499c..dc61b9bac 100644 --- a/src/plugins/streamers/oasis/lay_plugin/layOASISReaderPlugin.cc +++ b/src/plugins/streamers/oasis/lay_plugin/layOASISReaderPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/lay_plugin/layOASISReaderPlugin.h b/src/plugins/streamers/oasis/lay_plugin/layOASISReaderPlugin.h index 69776b632..28f3124f0 100644 --- a/src/plugins/streamers/oasis/lay_plugin/layOASISReaderPlugin.h +++ b/src/plugins/streamers/oasis/lay_plugin/layOASISReaderPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/lay_plugin/layOASISWriterPlugin.cc b/src/plugins/streamers/oasis/lay_plugin/layOASISWriterPlugin.cc index 329da288c..9dfba26c6 100644 --- a/src/plugins/streamers/oasis/lay_plugin/layOASISWriterPlugin.cc +++ b/src/plugins/streamers/oasis/lay_plugin/layOASISWriterPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/lay_plugin/layOASISWriterPlugin.h b/src/plugins/streamers/oasis/lay_plugin/layOASISWriterPlugin.h index 4c5c10ff2..14b9a6905 100644 --- a/src/plugins/streamers/oasis/lay_plugin/layOASISWriterPlugin.h +++ b/src/plugins/streamers/oasis/lay_plugin/layOASISWriterPlugin.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISReader.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISReader.cc index d24706a77..7bdadbce8 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc index b9d518a59..68bec61d0 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriter2.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriter2.cc index 89f005365..1fce196c8 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriter2.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriter2.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberDrillFileReader.cc b/src/plugins/streamers/pcb/db_plugin/dbGerberDrillFileReader.cc index 9ff432138..5a91eda8f 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbGerberDrillFileReader.cc +++ b/src/plugins/streamers/pcb/db_plugin/dbGerberDrillFileReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberDrillFileReader.h b/src/plugins/streamers/pcb/db_plugin/dbGerberDrillFileReader.h index 8e68a43aa..fa7712a36 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbGerberDrillFileReader.h +++ b/src/plugins/streamers/pcb/db_plugin/dbGerberDrillFileReader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberImportData.cc b/src/plugins/streamers/pcb/db_plugin/dbGerberImportData.cc index e0db81c35..9f14ff970 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbGerberImportData.cc +++ b/src/plugins/streamers/pcb/db_plugin/dbGerberImportData.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberImportData.h b/src/plugins/streamers/pcb/db_plugin/dbGerberImportData.h index 192fb7a2d..2256d5f59 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbGerberImportData.h +++ b/src/plugins/streamers/pcb/db_plugin/dbGerberImportData.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc index e801f73b5..8f41b7bf5 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc +++ b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.h b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.h index 6b61d52e1..486615668 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.h +++ b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/db_plugin/dbRS274XApertures.cc b/src/plugins/streamers/pcb/db_plugin/dbRS274XApertures.cc index aafd43df1..57fbcc307 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbRS274XApertures.cc +++ b/src/plugins/streamers/pcb/db_plugin/dbRS274XApertures.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/db_plugin/dbRS274XApertures.h b/src/plugins/streamers/pcb/db_plugin/dbRS274XApertures.h index e97e28ab7..f6b896cd7 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbRS274XApertures.h +++ b/src/plugins/streamers/pcb/db_plugin/dbRS274XApertures.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.cc b/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.cc index 0f403a813..5710803c1 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.cc +++ b/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.h b/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.h index aff726cf3..d78cf91b0 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.h +++ b/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/lay_plugin/layGerberImport.cc b/src/plugins/streamers/pcb/lay_plugin/layGerberImport.cc index d500ee5a6..be50a87c2 100644 --- a/src/plugins/streamers/pcb/lay_plugin/layGerberImport.cc +++ b/src/plugins/streamers/pcb/lay_plugin/layGerberImport.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/lay_plugin/layGerberImportDialog.cc b/src/plugins/streamers/pcb/lay_plugin/layGerberImportDialog.cc index ff6eae34d..9816c4e41 100644 --- a/src/plugins/streamers/pcb/lay_plugin/layGerberImportDialog.cc +++ b/src/plugins/streamers/pcb/lay_plugin/layGerberImportDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/lay_plugin/layGerberImportDialog.h b/src/plugins/streamers/pcb/lay_plugin/layGerberImportDialog.h index 8854ce9a3..6c57dfb19 100644 --- a/src/plugins/streamers/pcb/lay_plugin/layGerberImportDialog.h +++ b/src/plugins/streamers/pcb/lay_plugin/layGerberImportDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/streamers/pcb/unit_tests/dbGerberImport.cc b/src/plugins/streamers/pcb/unit_tests/dbGerberImport.cc index 2b5a324ad..adbfbac46 100644 --- a/src/plugins/streamers/pcb/unit_tests/dbGerberImport.cc +++ b/src/plugins/streamers/pcb/unit_tests/dbGerberImport.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.cc b/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.cc index 7b82b35f3..bb8a41fee 100644 --- a/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.cc +++ b/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.h b/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.h index 2250313f0..b59bf7fb7 100644 --- a/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.h +++ b/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/bool/lay_plugin/layBooleanOperationsPlugin.cc b/src/plugins/tools/bool/lay_plugin/layBooleanOperationsPlugin.cc index f8ef971f9..f609b537f 100644 --- a/src/plugins/tools/bool/lay_plugin/layBooleanOperationsPlugin.cc +++ b/src/plugins/tools/bool/lay_plugin/layBooleanOperationsPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/diff/lay_plugin/layDiffPlugin.cc b/src/plugins/tools/diff/lay_plugin/layDiffPlugin.cc index 035fa32aa..b075df3fa 100644 --- a/src/plugins/tools/diff/lay_plugin/layDiffPlugin.cc +++ b/src/plugins/tools/diff/lay_plugin/layDiffPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc b/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc index c718a33da..05de9883d 100644 --- a/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc +++ b/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.h b/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.h index d903bbae3..1272964fd 100644 --- a/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.h +++ b/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/import/lay_plugin/layStreamImport.cc b/src/plugins/tools/import/lay_plugin/layStreamImport.cc index b7aa911ae..99331d7d1 100644 --- a/src/plugins/tools/import/lay_plugin/layStreamImport.cc +++ b/src/plugins/tools/import/lay_plugin/layStreamImport.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/import/lay_plugin/layStreamImportDialog.cc b/src/plugins/tools/import/lay_plugin/layStreamImportDialog.cc index 8652fba39..d920ef3ef 100644 --- a/src/plugins/tools/import/lay_plugin/layStreamImportDialog.cc +++ b/src/plugins/tools/import/lay_plugin/layStreamImportDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/import/lay_plugin/layStreamImportDialog.h b/src/plugins/tools/import/lay_plugin/layStreamImportDialog.h index 636466bf3..7283d9ecb 100644 --- a/src/plugins/tools/import/lay_plugin/layStreamImportDialog.h +++ b/src/plugins/tools/import/lay_plugin/layStreamImportDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/import/lay_plugin/layStreamImporter.cc b/src/plugins/tools/import/lay_plugin/layStreamImporter.cc index 3deb6ea48..7f0f4a7c4 100644 --- a/src/plugins/tools/import/lay_plugin/layStreamImporter.cc +++ b/src/plugins/tools/import/lay_plugin/layStreamImporter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/import/lay_plugin/layStreamImporter.h b/src/plugins/tools/import/lay_plugin/layStreamImporter.h index 6f6cf6ad9..86e57d2ec 100644 --- a/src/plugins/tools/import/lay_plugin/layStreamImporter.h +++ b/src/plugins/tools/import/lay_plugin/layStreamImporter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc index 416430ace..b947d1e07 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h index 682e553d3..ce0f336b0 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc index bb44ab5bc..a55ffcc23 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h index 31e023580..03b32127c 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerPlugin.cc b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerPlugin.cc index b95f9a605..95bf0b933 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerPlugin.cc +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc b/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc index 0a12bca78..a19d6a066 100644 --- a/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc +++ b/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc @@ -1,7 +1,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConfig.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConfig.cc index 755bb6a1e..3fae044dd 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConfig.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConfig.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConfig.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConfig.h index ab4987522..b3c0312f9 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConfig.h +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConfig.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc index dadf49334..c43ec76f1 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h index 43da7ecda..12cbcd2ed 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.cc index a2e7c9799..53f420a5d 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.h index f11568eeb..556eeb9f2 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.h +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerPlugin.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerPlugin.cc index c7e0fcdba..b6789c7a5 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerPlugin.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc b/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc index 47e42d701..179f38584 100644 --- a/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc +++ b/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/xor/lay_plugin/layXORPlugin.cc b/src/plugins/tools/xor/lay_plugin/layXORPlugin.cc index 1610716ed..2b8c7784f 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORPlugin.cc +++ b/src/plugins/tools/xor/lay_plugin/layXORPlugin.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/xor/lay_plugin/layXORProgress.cc b/src/plugins/tools/xor/lay_plugin/layXORProgress.cc index 032cfbe5e..309847f4d 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORProgress.cc +++ b/src/plugins/tools/xor/lay_plugin/layXORProgress.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/xor/lay_plugin/layXORProgress.h b/src/plugins/tools/xor/lay_plugin/layXORProgress.h index 14741ddf4..d8de8f735 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORProgress.h +++ b/src/plugins/tools/xor/lay_plugin/layXORProgress.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc index 0c917b054..debaba9d6 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc +++ b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.h b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.h index ca09e77d8..65028d217 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.h +++ b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pya.cc b/src/pya/pya/pya.cc index f1308886f..f32a1a32d 100644 --- a/src/pya/pya/pya.cc +++ b/src/pya/pya/pya.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pya.h b/src/pya/pya/pya.h index e862f2812..85c4664b1 100644 --- a/src/pya/pya/pya.h +++ b/src/pya/pya/pya.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaCommon.h b/src/pya/pya/pyaCommon.h index e499da69b..ba16989c2 100644 --- a/src/pya/pya/pyaCommon.h +++ b/src/pya/pya/pyaCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaConvert.cc b/src/pya/pya/pyaConvert.cc index 41d28cf2c..4ceff8ecd 100644 --- a/src/pya/pya/pyaConvert.cc +++ b/src/pya/pya/pyaConvert.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaConvert.h b/src/pya/pya/pyaConvert.h index f64ab714d..4f24c077c 100644 --- a/src/pya/pya/pyaConvert.h +++ b/src/pya/pya/pyaConvert.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaHelpers.cc b/src/pya/pya/pyaHelpers.cc index e385c6498..01e4858ac 100644 --- a/src/pya/pya/pyaHelpers.cc +++ b/src/pya/pya/pyaHelpers.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaHelpers.h b/src/pya/pya/pyaHelpers.h index 8412bad75..9bfac61c1 100644 --- a/src/pya/pya/pyaHelpers.h +++ b/src/pya/pya/pyaHelpers.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaInspector.cc b/src/pya/pya/pyaInspector.cc index d60bb984d..5a27292bf 100644 --- a/src/pya/pya/pyaInspector.cc +++ b/src/pya/pya/pyaInspector.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaInspector.h b/src/pya/pya/pyaInspector.h index 71f8cc49f..ab47909ba 100644 --- a/src/pya/pya/pyaInspector.h +++ b/src/pya/pya/pyaInspector.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index 0b265280d..4a906cd9b 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaMarshal.h b/src/pya/pya/pyaMarshal.h index 96da170d9..2108013a7 100644 --- a/src/pya/pya/pyaMarshal.h +++ b/src/pya/pya/pyaMarshal.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index d5864e342..8d9fe9e22 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaModule.h b/src/pya/pya/pyaModule.h index f06ab4676..60c2d36a6 100644 --- a/src/pya/pya/pyaModule.h +++ b/src/pya/pya/pyaModule.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaObject.cc b/src/pya/pya/pyaObject.cc index 8c1768e9b..56024228c 100644 --- a/src/pya/pya/pyaObject.cc +++ b/src/pya/pya/pyaObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaObject.h b/src/pya/pya/pyaObject.h index c1b4a0aaf..24772aab6 100644 --- a/src/pya/pya/pyaObject.h +++ b/src/pya/pya/pyaObject.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaRefs.cc b/src/pya/pya/pyaRefs.cc index bc134d84b..b2257b727 100644 --- a/src/pya/pya/pyaRefs.cc +++ b/src/pya/pya/pyaRefs.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaRefs.h b/src/pya/pya/pyaRefs.h index e1265b5aa..da2c3aef0 100644 --- a/src/pya/pya/pyaRefs.h +++ b/src/pya/pya/pyaRefs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaSignalHandler.cc b/src/pya/pya/pyaSignalHandler.cc index 88b7d9651..cf4e19aed 100644 --- a/src/pya/pya/pyaSignalHandler.cc +++ b/src/pya/pya/pyaSignalHandler.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaSignalHandler.h b/src/pya/pya/pyaSignalHandler.h index 751229ad0..743e9347d 100644 --- a/src/pya/pya/pyaSignalHandler.h +++ b/src/pya/pya/pyaSignalHandler.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaStatusChangedListener.cc b/src/pya/pya/pyaStatusChangedListener.cc index 37dcaed0e..4504f58d6 100644 --- a/src/pya/pya/pyaStatusChangedListener.cc +++ b/src/pya/pya/pyaStatusChangedListener.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaStatusChangedListener.h b/src/pya/pya/pyaStatusChangedListener.h index 6b781acdb..048bfbf92 100644 --- a/src/pya/pya/pyaStatusChangedListener.h +++ b/src/pya/pya/pyaStatusChangedListener.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaUtils.cc b/src/pya/pya/pyaUtils.cc index b53d72e86..dccad067d 100644 --- a/src/pya/pya/pyaUtils.cc +++ b/src/pya/pya/pyaUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/pya/pyaUtils.h b/src/pya/pya/pyaUtils.h index b4212afac..7f6c66d9d 100644 --- a/src/pya/pya/pyaUtils.h +++ b/src/pya/pya/pyaUtils.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pya/unit_tests/pya.cc b/src/pya/unit_tests/pya.cc index 45c263d28..5b8dfff27 100644 --- a/src/pya/unit_tests/pya.cc +++ b/src/pya/unit_tests/pya.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pyastub/pya.cc b/src/pyastub/pya.cc index fad0755f0..f9c239668 100644 --- a/src/pyastub/pya.cc +++ b/src/pyastub/pya.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pyastub/pya.h b/src/pyastub/pya.h index b7af4a1d7..7abb87f8c 100644 --- a/src/pyastub/pya.h +++ b/src/pyastub/pya.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pyastub/pyaCommon.h b/src/pyastub/pyaCommon.h index 90a30727c..b27b1a56a 100644 --- a/src/pyastub/pyaCommon.h +++ b/src/pyastub/pyaCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtCore/QtCoreMain.cc b/src/pymod/QtCore/QtCoreMain.cc index d3bf7d9c1..5e8be5607 100644 --- a/src/pymod/QtCore/QtCoreMain.cc +++ b/src/pymod/QtCore/QtCoreMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtDesigner/QtDesignerMain.cc b/src/pymod/QtDesigner/QtDesignerMain.cc index 1fab66e43..897939fb5 100644 --- a/src/pymod/QtDesigner/QtDesignerMain.cc +++ b/src/pymod/QtDesigner/QtDesignerMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtGui/QtGuiMain.cc b/src/pymod/QtGui/QtGuiMain.cc index c6a9f1d26..2cebf7048 100644 --- a/src/pymod/QtGui/QtGuiMain.cc +++ b/src/pymod/QtGui/QtGuiMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtMultimedia/QtMultimediaMain.cc b/src/pymod/QtMultimedia/QtMultimediaMain.cc index 321dbd69d..32c88593d 100644 --- a/src/pymod/QtMultimedia/QtMultimediaMain.cc +++ b/src/pymod/QtMultimedia/QtMultimediaMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtNetwork/QtNetworkMain.cc b/src/pymod/QtNetwork/QtNetworkMain.cc index ce53c598b..d4e8a5e47 100644 --- a/src/pymod/QtNetwork/QtNetworkMain.cc +++ b/src/pymod/QtNetwork/QtNetworkMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtPrintSupport/QtPrintSupportMain.cc b/src/pymod/QtPrintSupport/QtPrintSupportMain.cc index f216b01dc..0363802c3 100644 --- a/src/pymod/QtPrintSupport/QtPrintSupportMain.cc +++ b/src/pymod/QtPrintSupport/QtPrintSupportMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtSql/QtSqlMain.cc b/src/pymod/QtSql/QtSqlMain.cc index 0fb46fa02..95fc2036b 100644 --- a/src/pymod/QtSql/QtSqlMain.cc +++ b/src/pymod/QtSql/QtSqlMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtSvg/QtSvgMain.cc b/src/pymod/QtSvg/QtSvgMain.cc index 28ef8ca66..36acafd7b 100644 --- a/src/pymod/QtSvg/QtSvgMain.cc +++ b/src/pymod/QtSvg/QtSvgMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtWidgets/QtWidgetsMain.cc b/src/pymod/QtWidgets/QtWidgetsMain.cc index bfd46c577..137bf012f 100644 --- a/src/pymod/QtWidgets/QtWidgetsMain.cc +++ b/src/pymod/QtWidgets/QtWidgetsMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtXml/QtXmlMain.cc b/src/pymod/QtXml/QtXmlMain.cc index 87976b47a..52cbdbb68 100644 --- a/src/pymod/QtXml/QtXmlMain.cc +++ b/src/pymod/QtXml/QtXmlMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/QtXmlPatterns/QtXmlPatternsMain.cc b/src/pymod/QtXmlPatterns/QtXmlPatternsMain.cc index 9e6c8e92c..403294467 100644 --- a/src/pymod/QtXmlPatterns/QtXmlPatternsMain.cc +++ b/src/pymod/QtXmlPatterns/QtXmlPatternsMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/bridge_sample/bridge_sample.cc b/src/pymod/bridge_sample/bridge_sample.cc index 158627b5a..59d536b41 100644 --- a/src/pymod/bridge_sample/bridge_sample.cc +++ b/src/pymod/bridge_sample/bridge_sample.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/db/dbMain.cc b/src/pymod/db/dbMain.cc index bdb15f7df..0f1d57f5f 100644 --- a/src/pymod/db/dbMain.cc +++ b/src/pymod/db/dbMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/lay/layMain.cc b/src/pymod/lay/layMain.cc index 36f6e51a8..8a8492953 100644 --- a/src/pymod/lay/layMain.cc +++ b/src/pymod/lay/layMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/pymodHelper.h b/src/pymod/pymodHelper.h index ba2a69760..8dbb60c57 100644 --- a/src/pymod/pymodHelper.h +++ b/src/pymod/pymodHelper.h @@ -3,7 +3,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/rdb/rdbMain.cc b/src/pymod/rdb/rdbMain.cc index ba085fe97..6b554a16f 100644 --- a/src/pymod/rdb/rdbMain.cc +++ b/src/pymod/rdb/rdbMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/tl/tlMain.cc b/src/pymod/tl/tlMain.cc index 4efb94a35..e582ba80b 100644 --- a/src/pymod/tl/tlMain.cc +++ b/src/pymod/tl/tlMain.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/pymod/unit_tests/pymod_tests.cc b/src/pymod/unit_tests/pymod_tests.cc index 5db932d67..b9957f36f 100644 --- a/src/pymod/unit_tests/pymod_tests.cc +++ b/src/pymod/unit_tests/pymod_tests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 008349bd3..543965467 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rba.h b/src/rba/rba/rba.h index 748fdb1bb..17615acb3 100644 --- a/src/rba/rba/rba.h +++ b/src/rba/rba/rba.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaCommon.h b/src/rba/rba/rbaCommon.h index 7321434fe..038450d47 100644 --- a/src/rba/rba/rbaCommon.h +++ b/src/rba/rba/rbaCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaConvert.cc b/src/rba/rba/rbaConvert.cc index 1bab1ccd0..1210624c5 100644 --- a/src/rba/rba/rbaConvert.cc +++ b/src/rba/rba/rbaConvert.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaConvert.h b/src/rba/rba/rbaConvert.h index c334d6dc3..c04c41d98 100644 --- a/src/rba/rba/rbaConvert.h +++ b/src/rba/rba/rbaConvert.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaInspector.cc b/src/rba/rba/rbaInspector.cc index 70a7c9865..d4bec0268 100644 --- a/src/rba/rba/rbaInspector.cc +++ b/src/rba/rba/rbaInspector.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaInspector.h b/src/rba/rba/rbaInspector.h index 37cad7868..90c1691d5 100644 --- a/src/rba/rba/rbaInspector.h +++ b/src/rba/rba/rbaInspector.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaInternal.cc b/src/rba/rba/rbaInternal.cc index f5dc134a8..2e4ed4faf 100644 --- a/src/rba/rba/rbaInternal.cc +++ b/src/rba/rba/rbaInternal.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaInternal.h b/src/rba/rba/rbaInternal.h index cca746f6d..512ed98d0 100644 --- a/src/rba/rba/rbaInternal.h +++ b/src/rba/rba/rbaInternal.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index 3af484f31..7b0b0d9ad 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaMarshal.h b/src/rba/rba/rbaMarshal.h index 4608ae5f4..623fda033 100644 --- a/src/rba/rba/rbaMarshal.h +++ b/src/rba/rba/rbaMarshal.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaUtils.cc b/src/rba/rba/rbaUtils.cc index 7d8704acf..405bc51db 100644 --- a/src/rba/rba/rbaUtils.cc +++ b/src/rba/rba/rbaUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/rba/rbaUtils.h b/src/rba/rba/rbaUtils.h index 37a21d1aa..81fd2bc75 100644 --- a/src/rba/rba/rbaUtils.h +++ b/src/rba/rba/rbaUtils.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index 883df8009..f3e9cef00 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rbastub/rba.cc b/src/rbastub/rba.cc index 189e7d23c..567ac6e57 100644 --- a/src/rbastub/rba.cc +++ b/src/rbastub/rba.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rbastub/rba.h b/src/rbastub/rba.h index a9f73dad2..037a66ccd 100644 --- a/src/rbastub/rba.h +++ b/src/rbastub/rba.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rbastub/rbaCommon.h b/src/rbastub/rbaCommon.h index 7321434fe..038450d47 100644 --- a/src/rbastub/rbaCommon.h +++ b/src/rbastub/rbaCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index 7d49b9f37..928dd88c9 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index a8a8808da..2fe130fa1 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index f98742449..614ec8f1e 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbCommon.h b/src/rdb/rdb/rdbCommon.h index 696650adb..57d92b375 100644 --- a/src/rdb/rdb/rdbCommon.h +++ b/src/rdb/rdb/rdbCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbFile.cc b/src/rdb/rdb/rdbFile.cc index f0c0d9b33..4b6b5d1c9 100644 --- a/src/rdb/rdb/rdbFile.cc +++ b/src/rdb/rdb/rdbFile.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbForceLink.cc b/src/rdb/rdb/rdbForceLink.cc index 4c8b12ba3..79fd50235 100644 --- a/src/rdb/rdb/rdbForceLink.cc +++ b/src/rdb/rdb/rdbForceLink.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbForceLink.h b/src/rdb/rdb/rdbForceLink.h index 3badc6df5..8a1dc2ca6 100644 --- a/src/rdb/rdb/rdbForceLink.h +++ b/src/rdb/rdb/rdbForceLink.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbRVEReader.cc b/src/rdb/rdb/rdbRVEReader.cc index de2be226f..4421f2e39 100644 --- a/src/rdb/rdb/rdbRVEReader.cc +++ b/src/rdb/rdb/rdbRVEReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbReader.cc b/src/rdb/rdb/rdbReader.cc index 75410cb22..c32ca7f7e 100644 --- a/src/rdb/rdb/rdbReader.cc +++ b/src/rdb/rdb/rdbReader.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbReader.h b/src/rdb/rdb/rdbReader.h index 2eccfec10..57ef72907 100644 --- a/src/rdb/rdb/rdbReader.h +++ b/src/rdb/rdb/rdbReader.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbTiledRdbOutputReceiver.cc b/src/rdb/rdb/rdbTiledRdbOutputReceiver.cc index 0d5452b2d..8748fcd3a 100644 --- a/src/rdb/rdb/rdbTiledRdbOutputReceiver.cc +++ b/src/rdb/rdb/rdbTiledRdbOutputReceiver.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbTiledRdbOutputReceiver.h b/src/rdb/rdb/rdbTiledRdbOutputReceiver.h index d1b01fbf6..e3600a084 100644 --- a/src/rdb/rdb/rdbTiledRdbOutputReceiver.h +++ b/src/rdb/rdb/rdbTiledRdbOutputReceiver.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbUtils.cc b/src/rdb/rdb/rdbUtils.cc index 66f5d4aca..d0f83eadf 100644 --- a/src/rdb/rdb/rdbUtils.cc +++ b/src/rdb/rdb/rdbUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/rdb/rdbUtils.h b/src/rdb/rdb/rdbUtils.h index e5e96c92f..01bade4a8 100644 --- a/src/rdb/rdb/rdbUtils.h +++ b/src/rdb/rdb/rdbUtils.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/rdb/unit_tests/rdb.cc b/src/rdb/unit_tests/rdb.cc index fab2f3135..331901df6 100644 --- a/src/rdb/unit_tests/rdb.cc +++ b/src/rdb/unit_tests/rdb.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlArch.cc b/src/tl/tl/tlArch.cc index 837847bcd..ba65847e6 100644 --- a/src/tl/tl/tlArch.cc +++ b/src/tl/tl/tlArch.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlArch.h b/src/tl/tl/tlArch.h index 4d2642e2f..cb783d544 100644 --- a/src/tl/tl/tlArch.h +++ b/src/tl/tl/tlArch.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlAssert.cc b/src/tl/tl/tlAssert.cc index 06ab9a2ca..ef437515f 100644 --- a/src/tl/tl/tlAssert.cc +++ b/src/tl/tl/tlAssert.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlAssert.h b/src/tl/tl/tlAssert.h index e6edbe9bb..4b8c85808 100644 --- a/src/tl/tl/tlAssert.h +++ b/src/tl/tl/tlAssert.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlClassRegistry.cc b/src/tl/tl/tlClassRegistry.cc index 3fa5b39c9..01a0e4eb6 100644 --- a/src/tl/tl/tlClassRegistry.cc +++ b/src/tl/tl/tlClassRegistry.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlClassRegistry.h b/src/tl/tl/tlClassRegistry.h index fa4a360a7..428c4509c 100644 --- a/src/tl/tl/tlClassRegistry.h +++ b/src/tl/tl/tlClassRegistry.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlCommandLineParser.cc b/src/tl/tl/tlCommandLineParser.cc index c30eb5657..1df8e307c 100644 --- a/src/tl/tl/tlCommandLineParser.cc +++ b/src/tl/tl/tlCommandLineParser.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlCommandLineParser.h b/src/tl/tl/tlCommandLineParser.h index 206b8fc23..44c716c24 100644 --- a/src/tl/tl/tlCommandLineParser.h +++ b/src/tl/tl/tlCommandLineParser.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlCommon.h b/src/tl/tl/tlCommon.h index 4049a3bd7..0b6200b32 100644 --- a/src/tl/tl/tlCommon.h +++ b/src/tl/tl/tlCommon.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlCpp.h b/src/tl/tl/tlCpp.h index 2887b67c1..4bb6d647d 100644 --- a/src/tl/tl/tlCpp.h +++ b/src/tl/tl/tlCpp.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlDataMapping.cc b/src/tl/tl/tlDataMapping.cc index 53cfd4fab..4fb9d3c60 100644 --- a/src/tl/tl/tlDataMapping.cc +++ b/src/tl/tl/tlDataMapping.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlDataMapping.h b/src/tl/tl/tlDataMapping.h index 252c17ce4..eab82dd69 100644 --- a/src/tl/tl/tlDataMapping.h +++ b/src/tl/tl/tlDataMapping.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlDeferredExecution.cc b/src/tl/tl/tlDeferredExecution.cc index 6f93f7bb2..85dc77598 100644 --- a/src/tl/tl/tlDeferredExecution.cc +++ b/src/tl/tl/tlDeferredExecution.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlDeferredExecution.h b/src/tl/tl/tlDeferredExecution.h index 3fee3969b..55b08e895 100644 --- a/src/tl/tl/tlDeferredExecution.h +++ b/src/tl/tl/tlDeferredExecution.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlDeferredExecutionQt.cc b/src/tl/tl/tlDeferredExecutionQt.cc index 1b0bbbefa..d05381071 100644 --- a/src/tl/tl/tlDeferredExecutionQt.cc +++ b/src/tl/tl/tlDeferredExecutionQt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlDeferredExecutionQt.h b/src/tl/tl/tlDeferredExecutionQt.h index 044aa9328..80b77ed6c 100644 --- a/src/tl/tl/tlDeferredExecutionQt.h +++ b/src/tl/tl/tlDeferredExecutionQt.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlDeflate.cc b/src/tl/tl/tlDeflate.cc index 9a7d5f167..2e7c5a2f4 100644 --- a/src/tl/tl/tlDeflate.cc +++ b/src/tl/tl/tlDeflate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlDeflate.h b/src/tl/tl/tlDeflate.h index f7d69ef4a..e4e0cae0e 100644 --- a/src/tl/tl/tlDeflate.h +++ b/src/tl/tl/tlDeflate.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlDefs.h b/src/tl/tl/tlDefs.h index 4197cb53b..74604eb08 100644 --- a/src/tl/tl/tlDefs.h +++ b/src/tl/tl/tlDefs.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlEvents.cc b/src/tl/tl/tlEvents.cc index a282a483d..749b7d881 100644 --- a/src/tl/tl/tlEvents.cc +++ b/src/tl/tl/tlEvents.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlEvents.h b/src/tl/tl/tlEvents.h index 8a1c9a00e..7c38eba15 100644 --- a/src/tl/tl/tlEvents.h +++ b/src/tl/tl/tlEvents.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlEventsVar.h b/src/tl/tl/tlEventsVar.h index 37ddbf7c6..0a39e63f7 100644 --- a/src/tl/tl/tlEventsVar.h +++ b/src/tl/tl/tlEventsVar.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlException.cc b/src/tl/tl/tlException.cc index 9e405fd95..978293eb4 100644 --- a/src/tl/tl/tlException.cc +++ b/src/tl/tl/tlException.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlException.h b/src/tl/tl/tlException.h index d324806bf..6b54953e6 100644 --- a/src/tl/tl/tlException.h +++ b/src/tl/tl/tlException.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlExceptions.cc b/src/tl/tl/tlExceptions.cc index 0d546f63a..852dc8323 100644 --- a/src/tl/tl/tlExceptions.cc +++ b/src/tl/tl/tlExceptions.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlExceptions.h b/src/tl/tl/tlExceptions.h index 6e8974877..feade656a 100644 --- a/src/tl/tl/tlExceptions.h +++ b/src/tl/tl/tlExceptions.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index 6aa6a823b..9cdd45452 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlExpression.h b/src/tl/tl/tlExpression.h index 3cb5e4faf..013f9a912 100644 --- a/src/tl/tl/tlExpression.h +++ b/src/tl/tl/tlExpression.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlFileSystemWatcher.cc b/src/tl/tl/tlFileSystemWatcher.cc index f865c71be..59aa075dc 100644 --- a/src/tl/tl/tlFileSystemWatcher.cc +++ b/src/tl/tl/tlFileSystemWatcher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlFileSystemWatcher.h b/src/tl/tl/tlFileSystemWatcher.h index 7f465ce89..c7cfe6735 100644 --- a/src/tl/tl/tlFileSystemWatcher.h +++ b/src/tl/tl/tlFileSystemWatcher.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index 1176da22b..79adf0155 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlFileUtils.h b/src/tl/tl/tlFileUtils.h index b338822e1..da2470591 100644 --- a/src/tl/tl/tlFileUtils.h +++ b/src/tl/tl/tlFileUtils.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlFixedVector.h b/src/tl/tl/tlFixedVector.h index 0b99837e0..1bed36e0f 100644 --- a/src/tl/tl/tlFixedVector.h +++ b/src/tl/tl/tlFixedVector.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlGlobPattern.cc b/src/tl/tl/tlGlobPattern.cc index 46e98f528..179c139fd 100644 --- a/src/tl/tl/tlGlobPattern.cc +++ b/src/tl/tl/tlGlobPattern.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlGlobPattern.h b/src/tl/tl/tlGlobPattern.h index 695c71c80..7d4dcd430 100644 --- a/src/tl/tl/tlGlobPattern.h +++ b/src/tl/tl/tlGlobPattern.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlHeap.cc b/src/tl/tl/tlHeap.cc index f40c236aa..727e3e15d 100644 --- a/src/tl/tl/tlHeap.cc +++ b/src/tl/tl/tlHeap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlHeap.h b/src/tl/tl/tlHeap.h index 22628ec88..32fbba814 100644 --- a/src/tl/tl/tlHeap.h +++ b/src/tl/tl/tlHeap.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlHttpStream.cc b/src/tl/tl/tlHttpStream.cc index b2268cca8..3bed9982b 100644 --- a/src/tl/tl/tlHttpStream.cc +++ b/src/tl/tl/tlHttpStream.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlHttpStream.h b/src/tl/tl/tlHttpStream.h index 8d3c1149b..430f0ab42 100644 --- a/src/tl/tl/tlHttpStream.h +++ b/src/tl/tl/tlHttpStream.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlHttpStreamCurl.cc b/src/tl/tl/tlHttpStreamCurl.cc index 4d1bb1a86..4d8a39349 100644 --- a/src/tl/tl/tlHttpStreamCurl.cc +++ b/src/tl/tl/tlHttpStreamCurl.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlHttpStreamCurl.h b/src/tl/tl/tlHttpStreamCurl.h index 784d1875b..d0a6bbe3b 100644 --- a/src/tl/tl/tlHttpStreamCurl.h +++ b/src/tl/tl/tlHttpStreamCurl.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlHttpStreamNoQt.cc b/src/tl/tl/tlHttpStreamNoQt.cc index 976111907..69b6bd488 100644 --- a/src/tl/tl/tlHttpStreamNoQt.cc +++ b/src/tl/tl/tlHttpStreamNoQt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlHttpStreamQt.cc b/src/tl/tl/tlHttpStreamQt.cc index 3f70e60af..6bff6d7b0 100644 --- a/src/tl/tl/tlHttpStreamQt.cc +++ b/src/tl/tl/tlHttpStreamQt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlHttpStreamQt.h b/src/tl/tl/tlHttpStreamQt.h index 6332ec81b..df975789c 100644 --- a/src/tl/tl/tlHttpStreamQt.h +++ b/src/tl/tl/tlHttpStreamQt.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlInt128Support.cc b/src/tl/tl/tlInt128Support.cc index c6f4bd007..65653e4d1 100644 --- a/src/tl/tl/tlInt128Support.cc +++ b/src/tl/tl/tlInt128Support.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlInt128Support.h b/src/tl/tl/tlInt128Support.h index 1ead22cc6..40af6a956 100644 --- a/src/tl/tl/tlInt128Support.h +++ b/src/tl/tl/tlInt128Support.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlInternational.cc b/src/tl/tl/tlInternational.cc index 231e03cc6..797ba0e9a 100644 --- a/src/tl/tl/tlInternational.cc +++ b/src/tl/tl/tlInternational.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlInternational.h b/src/tl/tl/tlInternational.h index f642b1bf3..9ae466860 100644 --- a/src/tl/tl/tlInternational.h +++ b/src/tl/tl/tlInternational.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlIntervalMap.h b/src/tl/tl/tlIntervalMap.h index 194d0e5ce..9b2655291 100644 --- a/src/tl/tl/tlIntervalMap.h +++ b/src/tl/tl/tlIntervalMap.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlIntervalSet.h b/src/tl/tl/tlIntervalSet.h index 5f2c7b0ba..b201ee1ff 100644 --- a/src/tl/tl/tlIntervalSet.h +++ b/src/tl/tl/tlIntervalSet.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlKDTree.h b/src/tl/tl/tlKDTree.h index 4a2b2e365..e3744215c 100644 --- a/src/tl/tl/tlKDTree.h +++ b/src/tl/tl/tlKDTree.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlList.cc b/src/tl/tl/tlList.cc index fa9c84732..b7aa685a9 100644 --- a/src/tl/tl/tlList.cc +++ b/src/tl/tl/tlList.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlList.h b/src/tl/tl/tlList.h index ff66cb359..3d38dc95b 100644 --- a/src/tl/tl/tlList.h +++ b/src/tl/tl/tlList.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlLog.cc b/src/tl/tl/tlLog.cc index 402aacad6..a4d63953c 100644 --- a/src/tl/tl/tlLog.cc +++ b/src/tl/tl/tlLog.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlLog.h b/src/tl/tl/tlLog.h index 28bc567a0..daf03937d 100644 --- a/src/tl/tl/tlLog.h +++ b/src/tl/tl/tlLog.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlLongInt.cc b/src/tl/tl/tlLongInt.cc index ac0f4bbe7..4f7c63bd6 100644 --- a/src/tl/tl/tlLongInt.cc +++ b/src/tl/tl/tlLongInt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlLongInt.h b/src/tl/tl/tlLongInt.h index 7a56bd927..dada0487d 100644 --- a/src/tl/tl/tlLongInt.h +++ b/src/tl/tl/tlLongInt.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlMath.h b/src/tl/tl/tlMath.h index c0c4a8054..3d8d7824e 100644 --- a/src/tl/tl/tlMath.h +++ b/src/tl/tl/tlMath.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlObject.cc b/src/tl/tl/tlObject.cc index 305e0b3bb..45dbc2bba 100644 --- a/src/tl/tl/tlObject.cc +++ b/src/tl/tl/tlObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlObject.h b/src/tl/tl/tlObject.h index a66426675..5fabdb595 100644 --- a/src/tl/tl/tlObject.h +++ b/src/tl/tl/tlObject.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlObjectCollection.h b/src/tl/tl/tlObjectCollection.h index e72b451c2..cb9992c2c 100644 --- a/src/tl/tl/tlObjectCollection.h +++ b/src/tl/tl/tlObjectCollection.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlProgress.cc b/src/tl/tl/tlProgress.cc index 84df5ea01..2acae6f27 100644 --- a/src/tl/tl/tlProgress.cc +++ b/src/tl/tl/tlProgress.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlProgress.h b/src/tl/tl/tlProgress.h index 1f41abb43..0cab4587d 100644 --- a/src/tl/tl/tlProgress.h +++ b/src/tl/tl/tlProgress.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlReuseVector.h b/src/tl/tl/tlReuseVector.h index f43698c2c..f31236a3a 100644 --- a/src/tl/tl/tlReuseVector.h +++ b/src/tl/tl/tlReuseVector.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlScriptError.cc b/src/tl/tl/tlScriptError.cc index ae59b713b..f54573b0e 100644 --- a/src/tl/tl/tlScriptError.cc +++ b/src/tl/tl/tlScriptError.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlScriptError.h b/src/tl/tl/tlScriptError.h index a2870f215..0e7f0a70e 100644 --- a/src/tl/tl/tlScriptError.h +++ b/src/tl/tl/tlScriptError.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlStableVector.h b/src/tl/tl/tlStableVector.h index 6668a6423..018b301a3 100644 --- a/src/tl/tl/tlStableVector.h +++ b/src/tl/tl/tlStableVector.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlStaticObjects.cc b/src/tl/tl/tlStaticObjects.cc index fbbdba9a6..024a8a4d2 100644 --- a/src/tl/tl/tlStaticObjects.cc +++ b/src/tl/tl/tlStaticObjects.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlStaticObjects.h b/src/tl/tl/tlStaticObjects.h index 4b0c8873b..52601ad15 100644 --- a/src/tl/tl/tlStaticObjects.h +++ b/src/tl/tl/tlStaticObjects.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index 0ce17a848..e515a7021 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index db1db4012..f674e3c44 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index 9a7637299..ae5420eed 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlString.h b/src/tl/tl/tlString.h index c96a7096a..6f69aa1d5 100644 --- a/src/tl/tl/tlString.h +++ b/src/tl/tl/tlString.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlThreadedWorkers.cc b/src/tl/tl/tlThreadedWorkers.cc index 707f5a7b5..8f782dab2 100644 --- a/src/tl/tl/tlThreadedWorkers.cc +++ b/src/tl/tl/tlThreadedWorkers.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlThreadedWorkers.h b/src/tl/tl/tlThreadedWorkers.h index 0dc414b1d..44a130d14 100644 --- a/src/tl/tl/tlThreadedWorkers.h +++ b/src/tl/tl/tlThreadedWorkers.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlThreads.cc b/src/tl/tl/tlThreads.cc index 64be53464..da2c80bdb 100644 --- a/src/tl/tl/tlThreads.cc +++ b/src/tl/tl/tlThreads.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlThreads.h b/src/tl/tl/tlThreads.h index 9dbabd911..5418ff430 100644 --- a/src/tl/tl/tlThreads.h +++ b/src/tl/tl/tlThreads.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlTimer.cc b/src/tl/tl/tlTimer.cc index de04772ee..2ea649f35 100644 --- a/src/tl/tl/tlTimer.cc +++ b/src/tl/tl/tlTimer.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlTimer.h b/src/tl/tl/tlTimer.h index 14d3cbe3b..4ac115abc 100644 --- a/src/tl/tl/tlTimer.h +++ b/src/tl/tl/tlTimer.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlTypeTraits.h b/src/tl/tl/tlTypeTraits.h index c5b2367aa..472915bb4 100644 --- a/src/tl/tl/tlTypeTraits.h +++ b/src/tl/tl/tlTypeTraits.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlUniqueId.cc b/src/tl/tl/tlUniqueId.cc index 888b5b2b6..81eda206c 100644 --- a/src/tl/tl/tlUniqueId.cc +++ b/src/tl/tl/tlUniqueId.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlUniqueId.h b/src/tl/tl/tlUniqueId.h index 61a73c003..5184e62c8 100644 --- a/src/tl/tl/tlUniqueId.h +++ b/src/tl/tl/tlUniqueId.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlUnitTest.cc b/src/tl/tl/tlUnitTest.cc index b3a46e3f1..17cf07a63 100644 --- a/src/tl/tl/tlUnitTest.cc +++ b/src/tl/tl/tlUnitTest.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlUnitTest.h b/src/tl/tl/tlUnitTest.h index 9f1860bc7..dcd277dd5 100644 --- a/src/tl/tl/tlUnitTest.h +++ b/src/tl/tl/tlUnitTest.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlUri.cc b/src/tl/tl/tlUri.cc index 1137cba69..4325f6740 100644 --- a/src/tl/tl/tlUri.cc +++ b/src/tl/tl/tlUri.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlUri.h b/src/tl/tl/tlUri.h index 704bd9d50..1c64f4378 100644 --- a/src/tl/tl/tlUri.h +++ b/src/tl/tl/tlUri.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlUtils.h b/src/tl/tl/tlUtils.h index 84bdf5a5f..f39e6bd34 100644 --- a/src/tl/tl/tlUtils.h +++ b/src/tl/tl/tlUtils.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlVariant.cc b/src/tl/tl/tlVariant.cc index fb5ab023e..29e08caa5 100644 --- a/src/tl/tl/tlVariant.cc +++ b/src/tl/tl/tlVariant.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlVariant.h b/src/tl/tl/tlVariant.h index 587a57293..3821bea7e 100644 --- a/src/tl/tl/tlVariant.h +++ b/src/tl/tl/tlVariant.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlVariantUserClasses.h b/src/tl/tl/tlVariantUserClasses.h index 4933de943..0a5158187 100644 --- a/src/tl/tl/tlVariantUserClasses.h +++ b/src/tl/tl/tlVariantUserClasses.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlVector.h b/src/tl/tl/tlVector.h index 59731ed29..d863eba95 100644 --- a/src/tl/tl/tlVector.h +++ b/src/tl/tl/tlVector.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlWebDAV.cc b/src/tl/tl/tlWebDAV.cc index 7686fc18d..30f90bc53 100644 --- a/src/tl/tl/tlWebDAV.cc +++ b/src/tl/tl/tlWebDAV.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlWebDAV.h b/src/tl/tl/tlWebDAV.h index fef7f7e7f..359ae9a9c 100644 --- a/src/tl/tl/tlWebDAV.h +++ b/src/tl/tl/tlWebDAV.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlXMLParser.cc b/src/tl/tl/tlXMLParser.cc index adb5826ec..5fd8e9153 100644 --- a/src/tl/tl/tlXMLParser.cc +++ b/src/tl/tl/tlXMLParser.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlXMLParser.h b/src/tl/tl/tlXMLParser.h index 1e8bc1766..6c0bcfc36 100644 --- a/src/tl/tl/tlXMLParser.h +++ b/src/tl/tl/tlXMLParser.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlXMLWriter.cc b/src/tl/tl/tlXMLWriter.cc index c5835dea1..8c1660232 100644 --- a/src/tl/tl/tlXMLWriter.cc +++ b/src/tl/tl/tlXMLWriter.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/tl/tlXMLWriter.h b/src/tl/tl/tlXMLWriter.h index c0cdbc87e..c9d2da6bf 100644 --- a/src/tl/tl/tlXMLWriter.h +++ b/src/tl/tl/tlXMLWriter.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlAlgorithm.cc b/src/tl/unit_tests/tlAlgorithm.cc index 12e8e07c2..aedb42ce7 100644 --- a/src/tl/unit_tests/tlAlgorithm.cc +++ b/src/tl/unit_tests/tlAlgorithm.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlClassRegistry.cc b/src/tl/unit_tests/tlClassRegistry.cc index 4ee757488..33cb959c1 100644 --- a/src/tl/unit_tests/tlClassRegistry.cc +++ b/src/tl/unit_tests/tlClassRegistry.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlCommandLineParser.cc b/src/tl/unit_tests/tlCommandLineParser.cc index aace2fad4..6f6815623 100644 --- a/src/tl/unit_tests/tlCommandLineParser.cc +++ b/src/tl/unit_tests/tlCommandLineParser.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlDataMapping.cc b/src/tl/unit_tests/tlDataMapping.cc index 17d86fb3e..7ae92ade3 100644 --- a/src/tl/unit_tests/tlDataMapping.cc +++ b/src/tl/unit_tests/tlDataMapping.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlDeferredExecution.cc b/src/tl/unit_tests/tlDeferredExecution.cc index 71fcfed14..dd4ff17ed 100644 --- a/src/tl/unit_tests/tlDeferredExecution.cc +++ b/src/tl/unit_tests/tlDeferredExecution.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlDeflate.cc b/src/tl/unit_tests/tlDeflate.cc index d84593a70..ab5bb539d 100644 --- a/src/tl/unit_tests/tlDeflate.cc +++ b/src/tl/unit_tests/tlDeflate.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlEvents.cc b/src/tl/unit_tests/tlEvents.cc index 33f1593c8..9c7a5b7fe 100644 --- a/src/tl/unit_tests/tlEvents.cc +++ b/src/tl/unit_tests/tlEvents.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlExpression.cc b/src/tl/unit_tests/tlExpression.cc index 7c539cb9c..9abfd2b52 100644 --- a/src/tl/unit_tests/tlExpression.cc +++ b/src/tl/unit_tests/tlExpression.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlFileSystemWatcher.cc b/src/tl/unit_tests/tlFileSystemWatcher.cc index 76a5c6ebe..f0d68e28e 100644 --- a/src/tl/unit_tests/tlFileSystemWatcher.cc +++ b/src/tl/unit_tests/tlFileSystemWatcher.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlFileUtils.cc b/src/tl/unit_tests/tlFileUtils.cc index c49bc77ab..bf3f2ea9f 100644 --- a/src/tl/unit_tests/tlFileUtils.cc +++ b/src/tl/unit_tests/tlFileUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlGlobPattern.cc b/src/tl/unit_tests/tlGlobPattern.cc index 54ae8c55a..c39bca6d6 100644 --- a/src/tl/unit_tests/tlGlobPattern.cc +++ b/src/tl/unit_tests/tlGlobPattern.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlHttpStream.cc b/src/tl/unit_tests/tlHttpStream.cc index 0c45328da..b0cd49aa4 100644 --- a/src/tl/unit_tests/tlHttpStream.cc +++ b/src/tl/unit_tests/tlHttpStream.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlInt128Support.cc b/src/tl/unit_tests/tlInt128Support.cc index bca43995e..e5909e1dd 100644 --- a/src/tl/unit_tests/tlInt128Support.cc +++ b/src/tl/unit_tests/tlInt128Support.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlIntervalMap.cc b/src/tl/unit_tests/tlIntervalMap.cc index b4fdf916e..cc7393859 100644 --- a/src/tl/unit_tests/tlIntervalMap.cc +++ b/src/tl/unit_tests/tlIntervalMap.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlIntervalSet.cc b/src/tl/unit_tests/tlIntervalSet.cc index 95fc715c6..71778b02d 100644 --- a/src/tl/unit_tests/tlIntervalSet.cc +++ b/src/tl/unit_tests/tlIntervalSet.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlKDTree.cc b/src/tl/unit_tests/tlKDTree.cc index 307070326..10165f9b0 100644 --- a/src/tl/unit_tests/tlKDTree.cc +++ b/src/tl/unit_tests/tlKDTree.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlListTests.cc b/src/tl/unit_tests/tlListTests.cc index 5fde89b69..c6831c702 100644 --- a/src/tl/unit_tests/tlListTests.cc +++ b/src/tl/unit_tests/tlListTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlLongInt.cc b/src/tl/unit_tests/tlLongInt.cc index 3620e68e9..92e61b4fb 100644 --- a/src/tl/unit_tests/tlLongInt.cc +++ b/src/tl/unit_tests/tlLongInt.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlMath.cc b/src/tl/unit_tests/tlMath.cc index 5e39e9b2e..a290840f2 100644 --- a/src/tl/unit_tests/tlMath.cc +++ b/src/tl/unit_tests/tlMath.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlObject.cc b/src/tl/unit_tests/tlObject.cc index dcc69a2c3..a02d65dbc 100644 --- a/src/tl/unit_tests/tlObject.cc +++ b/src/tl/unit_tests/tlObject.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlReuseVector.cc b/src/tl/unit_tests/tlReuseVector.cc index f050288de..94f0ff543 100644 --- a/src/tl/unit_tests/tlReuseVector.cc +++ b/src/tl/unit_tests/tlReuseVector.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlStableVector.cc b/src/tl/unit_tests/tlStableVector.cc index 7688e27f4..142e8fbc9 100644 --- a/src/tl/unit_tests/tlStableVector.cc +++ b/src/tl/unit_tests/tlStableVector.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index 4a96013d1..e8d3dca15 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlString.cc b/src/tl/unit_tests/tlString.cc index a65c2f71c..846e73956 100644 --- a/src/tl/unit_tests/tlString.cc +++ b/src/tl/unit_tests/tlString.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlThreadedWorkers.cc b/src/tl/unit_tests/tlThreadedWorkers.cc index a9d757461..221e3aca9 100644 --- a/src/tl/unit_tests/tlThreadedWorkers.cc +++ b/src/tl/unit_tests/tlThreadedWorkers.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlThreads.cc b/src/tl/unit_tests/tlThreads.cc index 632f0824b..ff69e164f 100644 --- a/src/tl/unit_tests/tlThreads.cc +++ b/src/tl/unit_tests/tlThreads.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlUniqueIdTests.cc b/src/tl/unit_tests/tlUniqueIdTests.cc index 20d734939..ef3290436 100644 --- a/src/tl/unit_tests/tlUniqueIdTests.cc +++ b/src/tl/unit_tests/tlUniqueIdTests.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlUri.cc b/src/tl/unit_tests/tlUri.cc index 4d138f44e..1ecdd82d6 100644 --- a/src/tl/unit_tests/tlUri.cc +++ b/src/tl/unit_tests/tlUri.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlUtils.cc b/src/tl/unit_tests/tlUtils.cc index 8c942c090..c04731615 100644 --- a/src/tl/unit_tests/tlUtils.cc +++ b/src/tl/unit_tests/tlUtils.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlVariant.cc b/src/tl/unit_tests/tlVariant.cc index 7112523ca..bf2bc6391 100644 --- a/src/tl/unit_tests/tlVariant.cc +++ b/src/tl/unit_tests/tlVariant.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlWebDAV.cc b/src/tl/unit_tests/tlWebDAV.cc index beccaec35..26b4e9e98 100644 --- a/src/tl/unit_tests/tlWebDAV.cc +++ b/src/tl/unit_tests/tlWebDAV.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/tl/unit_tests/tlXMLParser.cc b/src/tl/unit_tests/tlXMLParser.cc index 20b1b0f31..2c4612f5c 100644 --- a/src/tl/unit_tests/tlXMLParser.cc +++ b/src/tl/unit_tests/tlXMLParser.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/unit_tests/unit_test_main.cc b/src/unit_tests/unit_test_main.cc index da260797c..5225cdee4 100644 --- a/src/unit_tests/unit_test_main.cc +++ b/src/unit_tests/unit_test_main.cc @@ -3,7 +3,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/unit_tests/utTestConsole.cc b/src/unit_tests/utTestConsole.cc index 343ed4d6e..c92b7ffc1 100644 --- a/src/unit_tests/utTestConsole.cc +++ b/src/unit_tests/utTestConsole.cc @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/unit_tests/utTestConsole.h b/src/unit_tests/utTestConsole.h index 2ba63e1dc..f5c3aae49 100644 --- a/src/unit_tests/utTestConsole.h +++ b/src/unit_tests/utTestConsole.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 diff --git a/src/version/version.h b/src/version/version.h index 9fc3de9d1..20706008b 100644 --- a/src/version/version.h +++ b/src/version/version.h @@ -2,7 +2,7 @@ /* KLayout Layout Viewer - Copyright (C) 2006-2018 Matthias Koefferlein + 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 @@ -52,7 +52,7 @@ const char *prg_about_text = "For feedback and bug reports mail to: contact@klayout.de\n" "\n" "\n" - "Copyright (C) 2006-2018 Matthias K\303\266fferlein\n" + "Copyright (C) 2006-2019 Matthias K\303\266fferlein\n" "\n" "This program is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" diff --git a/testdata/klayout_main/main.rb b/testdata/klayout_main/main.rb index 20e7ef5d3..5c0e06264 100644 --- a/testdata/klayout_main/main.rb +++ b/testdata/klayout_main/main.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/bridge.py b/testdata/pymod/bridge.py index e7fcd95d7..1f7d6a86a 100755 --- a/testdata/pymod/bridge.py +++ b/testdata/pymod/bridge.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtCore.py b/testdata/pymod/import_QtCore.py index b965ab15a..f066f0321 100755 --- a/testdata/pymod/import_QtCore.py +++ b/testdata/pymod/import_QtCore.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtDesigner.py b/testdata/pymod/import_QtDesigner.py index 8bfffb718..778dace96 100755 --- a/testdata/pymod/import_QtDesigner.py +++ b/testdata/pymod/import_QtDesigner.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtGui.py b/testdata/pymod/import_QtGui.py index 60d53d470..688e8dff6 100755 --- a/testdata/pymod/import_QtGui.py +++ b/testdata/pymod/import_QtGui.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtMultimedia.py b/testdata/pymod/import_QtMultimedia.py index d811d318a..cc7409e38 100755 --- a/testdata/pymod/import_QtMultimedia.py +++ b/testdata/pymod/import_QtMultimedia.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtNetwork.py b/testdata/pymod/import_QtNetwork.py index 140687f60..8cb6e9c17 100755 --- a/testdata/pymod/import_QtNetwork.py +++ b/testdata/pymod/import_QtNetwork.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtPrintSupport.py b/testdata/pymod/import_QtPrintSupport.py index 19c10aacb..80d150f55 100755 --- a/testdata/pymod/import_QtPrintSupport.py +++ b/testdata/pymod/import_QtPrintSupport.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtSql.py b/testdata/pymod/import_QtSql.py index 31f5211ce..721d0189f 100755 --- a/testdata/pymod/import_QtSql.py +++ b/testdata/pymod/import_QtSql.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtSvg.py b/testdata/pymod/import_QtSvg.py index 5e12aea46..a10240393 100755 --- a/testdata/pymod/import_QtSvg.py +++ b/testdata/pymod/import_QtSvg.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtWidgets.py b/testdata/pymod/import_QtWidgets.py index e14a776aa..f26e5c65c 100755 --- a/testdata/pymod/import_QtWidgets.py +++ b/testdata/pymod/import_QtWidgets.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtXml.py b/testdata/pymod/import_QtXml.py index 98f76e03f..73e314d91 100755 --- a/testdata/pymod/import_QtXml.py +++ b/testdata/pymod/import_QtXml.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_QtXmlPatterns.py b/testdata/pymod/import_QtXmlPatterns.py index c7305256a..426180206 100755 --- a/testdata/pymod/import_QtXmlPatterns.py +++ b/testdata/pymod/import_QtXmlPatterns.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_db.py b/testdata/pymod/import_db.py index 7bb5b97fc..8095088c5 100755 --- a/testdata/pymod/import_db.py +++ b/testdata/pymod/import_db.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_lay.py b/testdata/pymod/import_lay.py index b34b11e04..1e1835dae 100755 --- a/testdata/pymod/import_lay.py +++ b/testdata/pymod/import_lay.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_lay_noqt.py b/testdata/pymod/import_lay_noqt.py index 6a2d0fbfc..37e384359 100755 --- a/testdata/pymod/import_lay_noqt.py +++ b/testdata/pymod/import_lay_noqt.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_rdb.py b/testdata/pymod/import_rdb.py index 52638a8fd..7be1132fd 100755 --- a/testdata/pymod/import_rdb.py +++ b/testdata/pymod/import_rdb.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/pymod/import_tl.py b/testdata/pymod/import_tl.py index 73c9a5958..b798bb18c 100755 --- a/testdata/pymod/import_tl.py +++ b/testdata/pymod/import_tl.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/python/basic.py b/testdata/python/basic.py index eb459411d..568a9ee16 100644 --- a/testdata/python/basic.py +++ b/testdata/python/basic.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/python/dbLayoutTest.py b/testdata/python/dbLayoutTest.py index b31cefef1..f38518963 100644 --- a/testdata/python/dbLayoutTest.py +++ b/testdata/python/dbLayoutTest.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/python/dbPCells.py b/testdata/python/dbPCells.py index 26d2a31e7..6a3693663 100644 --- a/testdata/python/dbPCells.py +++ b/testdata/python/dbPCells.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/python/dbPolygonTest.py b/testdata/python/dbPolygonTest.py index 97e19550c..c06a43aca 100644 --- a/testdata/python/dbPolygonTest.py +++ b/testdata/python/dbPolygonTest.py @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/python/dbReaders.py b/testdata/python/dbReaders.py index 9f8119d1a..2c33a78ea 100644 --- a/testdata/python/dbReaders.py +++ b/testdata/python/dbReaders.py @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/python/dbRegionTest.py b/testdata/python/dbRegionTest.py index 1de49db34..b0ff7daae 100644 --- a/testdata/python/dbRegionTest.py +++ b/testdata/python/dbRegionTest.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/python/dbTransTest.py b/testdata/python/dbTransTest.py index b3423b01e..c8a677adc 100644 --- a/testdata/python/dbTransTest.py +++ b/testdata/python/dbTransTest.py @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/python/qtbinding.py b/testdata/python/qtbinding.py index a077656e4..52f1c27e5 100644 --- a/testdata/python/qtbinding.py +++ b/testdata/python/qtbinding.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/python/tlTest.py b/testdata/python/tlTest.py index b3d9e958d..fb6b15474 100644 --- a/testdata/python/tlTest.py +++ b/testdata/python/tlTest.py @@ -1,5 +1,5 @@ # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/antTest.rb b/testdata/ruby/antTest.rb index 71367969e..9b7eaa6d7 100644 --- a/testdata/ruby/antTest.rb +++ b/testdata/ruby/antTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/basic.rb b/testdata/ruby/basic.rb index 3a4d3bfd0..852e58f24 100644 --- a/testdata/ruby/basic.rb +++ b/testdata/ruby/basic.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbBooleanTest.rb b/testdata/ruby/dbBooleanTest.rb index f478f8efa..16e540243 100644 --- a/testdata/ruby/dbBooleanTest.rb +++ b/testdata/ruby/dbBooleanTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbBoxTest.rb b/testdata/ruby/dbBoxTest.rb index e0efec180..540935daa 100644 --- a/testdata/ruby/dbBoxTest.rb +++ b/testdata/ruby/dbBoxTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbCellInstArrayTest.rb b/testdata/ruby/dbCellInstArrayTest.rb index eb0f8f36d..f30c5966b 100644 --- a/testdata/ruby/dbCellInstArrayTest.rb +++ b/testdata/ruby/dbCellInstArrayTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbCellMapping.rb b/testdata/ruby/dbCellMapping.rb index 4b52c8281..ca3667137 100644 --- a/testdata/ruby/dbCellMapping.rb +++ b/testdata/ruby/dbCellMapping.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbEdgePairTest.rb b/testdata/ruby/dbEdgePairTest.rb index 4b5c61294..2253aaa6a 100644 --- a/testdata/ruby/dbEdgePairTest.rb +++ b/testdata/ruby/dbEdgePairTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index da529901b..b3a3efd6d 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbEdgeTest.rb b/testdata/ruby/dbEdgeTest.rb index 0afc6aa18..fae599c25 100644 --- a/testdata/ruby/dbEdgeTest.rb +++ b/testdata/ruby/dbEdgeTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 4fea31683..872d24dc7 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbGlyphs.rb b/testdata/ruby/dbGlyphs.rb index 429405073..3d2ec37d1 100644 --- a/testdata/ruby/dbGlyphs.rb +++ b/testdata/ruby/dbGlyphs.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbInstElementTest.rb b/testdata/ruby/dbInstElementTest.rb index 4d73caaf1..b47434ae9 100644 --- a/testdata/ruby/dbInstElementTest.rb +++ b/testdata/ruby/dbInstElementTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbInstanceTest.rb b/testdata/ruby/dbInstanceTest.rb index 33a771409..4e6779a50 100644 --- a/testdata/ruby/dbInstanceTest.rb +++ b/testdata/ruby/dbInstanceTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbLayerMapping.rb b/testdata/ruby/dbLayerMapping.rb index a4c9bc550..7262a5d59 100644 --- a/testdata/ruby/dbLayerMapping.rb +++ b/testdata/ruby/dbLayerMapping.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbLayout.rb b/testdata/ruby/dbLayout.rb index 5527a99db..04b418eb9 100644 --- a/testdata/ruby/dbLayout.rb +++ b/testdata/ruby/dbLayout.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbLayoutDiff.rb b/testdata/ruby/dbLayoutDiff.rb index 0de9cee5a..d6e63e278 100644 --- a/testdata/ruby/dbLayoutDiff.rb +++ b/testdata/ruby/dbLayoutDiff.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbLayoutQuery.rb b/testdata/ruby/dbLayoutQuery.rb index 946ebc391..ec49d1c9c 100644 --- a/testdata/ruby/dbLayoutQuery.rb +++ b/testdata/ruby/dbLayoutQuery.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbLayoutTest.rb b/testdata/ruby/dbLayoutTest.rb index 79ede777c..e34d4855f 100644 --- a/testdata/ruby/dbLayoutTest.rb +++ b/testdata/ruby/dbLayoutTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index 54a764c77..6029a064c 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbMatrix.rb b/testdata/ruby/dbMatrix.rb index 4006274db..13a5e4209 100644 --- a/testdata/ruby/dbMatrix.rb +++ b/testdata/ruby/dbMatrix.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 812304085..35e68e8fc 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbNetlistDeviceClasses.rb b/testdata/ruby/dbNetlistDeviceClasses.rb index 7881a1f9e..5f64ff810 100644 --- a/testdata/ruby/dbNetlistDeviceClasses.rb +++ b/testdata/ruby/dbNetlistDeviceClasses.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbPCells.rb b/testdata/ruby/dbPCells.rb index 124217264..e30ccbb06 100644 --- a/testdata/ruby/dbPCells.rb +++ b/testdata/ruby/dbPCells.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbPathTest.rb b/testdata/ruby/dbPathTest.rb index bcd10b4d9..4b3b66382 100644 --- a/testdata/ruby/dbPathTest.rb +++ b/testdata/ruby/dbPathTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbPointTest.rb b/testdata/ruby/dbPointTest.rb index 791fd6bc0..87ce2a1db 100644 --- a/testdata/ruby/dbPointTest.rb +++ b/testdata/ruby/dbPointTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index eea71dac3..684c86756 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbReaders.rb b/testdata/ruby/dbReaders.rb index d11d30e21..6c7750dfa 100644 --- a/testdata/ruby/dbReaders.rb +++ b/testdata/ruby/dbReaders.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 493aa7c68..b073b857f 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index 27fbcd36c..f9b8277a8 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbSimplePolygonTest.rb b/testdata/ruby/dbSimplePolygonTest.rb index 3b59dd0ce..ef1b81211 100644 --- a/testdata/ruby/dbSimplePolygonTest.rb +++ b/testdata/ruby/dbSimplePolygonTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbTextTest.rb b/testdata/ruby/dbTextTest.rb index 44fbe53b7..ea35c7122 100644 --- a/testdata/ruby/dbTextTest.rb +++ b/testdata/ruby/dbTextTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbTilingProcessorTest.rb b/testdata/ruby/dbTilingProcessorTest.rb index 9f389d508..41fd1bfbc 100644 --- a/testdata/ruby/dbTilingProcessorTest.rb +++ b/testdata/ruby/dbTilingProcessorTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbTransTest.rb b/testdata/ruby/dbTransTest.rb index 0785b00d3..3bf82499c 100644 --- a/testdata/ruby/dbTransTest.rb +++ b/testdata/ruby/dbTransTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/dbVectorTest.rb b/testdata/ruby/dbVectorTest.rb index 439dee4d7..2f2baedb2 100644 --- a/testdata/ruby/dbVectorTest.rb +++ b/testdata/ruby/dbVectorTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/edtTest.rb b/testdata/ruby/edtTest.rb index 70a77f333..6708c6b6a 100644 --- a/testdata/ruby/edtTest.rb +++ b/testdata/ruby/edtTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/extNetTracer.rb b/testdata/ruby/extNetTracer.rb index 53ed29d86..16a02603e 100644 --- a/testdata/ruby/extNetTracer.rb +++ b/testdata/ruby/extNetTracer.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/imgObject.rb b/testdata/ruby/imgObject.rb index d44f58eaa..5842c867f 100644 --- a/testdata/ruby/imgObject.rb +++ b/testdata/ruby/imgObject.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/layLayers.rb b/testdata/ruby/layLayers.rb index fcf0d0386..5e8cea0d6 100644 --- a/testdata/ruby/layLayers.rb +++ b/testdata/ruby/layLayers.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/layLayoutView.rb b/testdata/ruby/layLayoutView.rb index e33e56065..3180b83c5 100644 --- a/testdata/ruby/layLayoutView.rb +++ b/testdata/ruby/layLayoutView.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/layMarkers.rb b/testdata/ruby/layMarkers.rb index 3ac7ec549..d51bbd15a 100644 --- a/testdata/ruby/layMarkers.rb +++ b/testdata/ruby/layMarkers.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/layMenuTest.rb b/testdata/ruby/layMenuTest.rb index d504c981e..a59356a0f 100644 --- a/testdata/ruby/layMenuTest.rb +++ b/testdata/ruby/layMenuTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/laySaveLayoutOptions.rb b/testdata/ruby/laySaveLayoutOptions.rb index 350791cea..ccaae2b17 100644 --- a/testdata/ruby/laySaveLayoutOptions.rb +++ b/testdata/ruby/laySaveLayoutOptions.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/laySession.rb b/testdata/ruby/laySession.rb index 7a5e95ccd..961d01349 100644 --- a/testdata/ruby/laySession.rb +++ b/testdata/ruby/laySession.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/layTechnologies.rb b/testdata/ruby/layTechnologies.rb index 91506bc15..186b35aae 100644 --- a/testdata/ruby/layTechnologies.rb +++ b/testdata/ruby/layTechnologies.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/qtbinding.rb b/testdata/ruby/qtbinding.rb index e8d044dee..f41382330 100644 --- a/testdata/ruby/qtbinding.rb +++ b/testdata/ruby/qtbinding.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index f03238f10..2aa57f370 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 diff --git a/testdata/ruby/tlTest.rb b/testdata/ruby/tlTest.rb index 1d9522b79..f10ceeb59 100644 --- a/testdata/ruby/tlTest.rb +++ b/testdata/ruby/tlTest.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # KLayout Layout Viewer -# Copyright (C) 2006-2018 Matthias Koefferlein +# 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 From 9fa561803413d91c5a97887ed624203478fe847e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Jan 2019 23:49:12 +0100 Subject: [PATCH 192/335] Added test for device combination. --- src/db/unit_tests/dbLayoutToNetlistTests.cc | 279 +++++++++++++++++- .../algo/device_extract_au5_with_rec_nets.gds | Bin 0 -> 73106 bytes testdata/algo/device_extract_l5.gds | Bin 0 -> 4934 bytes testdata/ruby/dbLayoutToNetlist.rb | 1 - 4 files changed, 273 insertions(+), 7 deletions(-) create mode 100644 testdata/algo/device_extract_au5_with_rec_nets.gds create mode 100644 testdata/algo/device_extract_l5.gds diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 91ccde1fe..250b91ac0 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -40,6 +40,11 @@ namespace { +static std::string qnet_name (const db::Net *net) +{ + return net ? net->qname () : "(null)"; +} + static std::string device_name (const db::Device &device) { if (device.name ().empty ()) { @@ -202,12 +207,6 @@ static void dump_recursive_nets_to_layout (const db::LayoutToNetlist &l2n, db::L } } -// TODO: may be useful elsewhere? -static std::string qnet_name (const db::Net *net) -{ - return net ? net->qname () : "(null)"; -} - static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) { unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); @@ -1358,3 +1357,271 @@ TEST(4_GlobalNetDeviceExtraction) EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I8"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); } + +TEST(5_DeviceExtractionWithDeviceCombination) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int pplus = define_layer (ly, lmap, 10); + unsigned int nplus = define_layer (ly, lmap, 11); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l5.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rbulk (l2n.make_layer (ly.insert_layer ())); + std::auto_ptr rnwell (l2n.make_layer (nwell)); + std::auto_ptr ractive (l2n.make_layer (active)); + std::auto_ptr rpplus (l2n.make_layer (pplus)); + std::auto_ptr rnplus (l2n.make_layer (nplus)); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell & *rpplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell & *rnplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion + unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie + unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpsd.insert_into (&ly, tc.cell_index (), lptie); + rnsd.insert_into (&ly, tc.cell_index (), lntie); + + // NOTE: the device extractor will add more debug layers for the transistors: + // 20/0 -> Diffusion + // 21/0 -> Gate + MOSFET4Extractor pmos_ex ("PMOS", &ly); + MOSFET4Extractor nmos_ex ("NMOS", &ly); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rdiff_cont, rptie); + l2n.connect (*rdiff_cont, rntie); + l2n.connect (*rnwell, rntie); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + l2n.connect_global (*rbulk, "BULK"); + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 201/0 -> Well + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + // 212/0 -> N tie + // 213/0 -> P tie + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (214, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); + dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (314, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I22,$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I23,$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I24,$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I25,$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I6,$3=$I5,$4=$I4,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=$1,IN=IN,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" + " DPMOS $1 (S=OUT,G=IN,D=VDD,B=$1) [L=0.25,W=1.75,AS=0.91875,AD=0.48125]\n" + " DPMOS $2 (S=VDD,G=IN,D=OUT,B=$1) [L=0.25,W=1.75,AS=0.48125,AD=0.91875]\n" + " DNMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) [L=0.25,W=1.75,AS=0.91875,AD=0.48125]\n" + " DNMOS $4 (S=VSS,G=IN,D=OUT,B=BULK) [L=0.25,W=1.75,AS=0.48125,AD=0.91875]\n" + " XTRANS $1 ($1=OUT,$2=VSS,$3=IN)\n" + " XTRANS $2 ($1=OUT,$2=VDD,$3=IN)\n" + " XTRANS $3 ($1=OUT,$2=VSS,$3=IN)\n" + " XTRANS $4 ($1=OUT,$2=VDD,$3=IN)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au5_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + + // do some probing before purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:BULK,VSS"); + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS'):\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I6,$3=$I5,$4=$I4,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=$1,IN=IN,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" + " DPMOS $1 (S=OUT,G=IN,D=VDD,B=$1) [L=0.25,W=3.5,AS=1.4,AD=1.4]\n" + " DNMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) [L=0.25,W=3.5,AS=1.4,AD=1.4]\n" + ); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + // the transistor which supplies this probe target has been optimized away by "purge". + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)"); +} diff --git a/testdata/algo/device_extract_au5_with_rec_nets.gds b/testdata/algo/device_extract_au5_with_rec_nets.gds new file mode 100644 index 0000000000000000000000000000000000000000..8cdea5c7454c884b42d682408306e032a8c3457c GIT binary patch literal 73106 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�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 Date: Thu, 10 Jan 2019 23:36:52 +0100 Subject: [PATCH 193/335] WIP: refactoring - singularization of classes in separate files. --- src/db/db/db.pro | 17 +- src/db/db/dbCircuit.cc | 567 ++++++++ src/db/db/dbCircuit.h | 634 ++++++++ src/db/db/dbDevice.cc | 167 +++ src/db/db/dbDevice.h | 217 +++ src/db/db/dbDeviceClass.cc | 139 ++ src/db/db/dbDeviceClass.h | 422 ++++++ src/db/db/dbNet.cc | 324 +++++ src/db/db/dbNet.h | 651 +++++++++ src/db/db/dbNetlist.cc | 1196 --------------- src/db/db/dbNetlist.h | 2082 +-------------------------- src/db/db/dbNetlistUtils.h | 127 ++ src/db/db/dbPin.cc | 43 + src/db/db/dbPin.h | 81 ++ src/db/db/dbSubCircuit.cc | 131 ++ src/db/db/dbSubCircuit.h | 216 +++ src/db/unit_tests/dbNetlistTests.cc | 360 ++--- 17 files changed, 3919 insertions(+), 3455 deletions(-) create mode 100644 src/db/db/dbCircuit.cc create mode 100644 src/db/db/dbCircuit.h create mode 100644 src/db/db/dbDevice.cc create mode 100644 src/db/db/dbDevice.h create mode 100644 src/db/db/dbDeviceClass.cc create mode 100644 src/db/db/dbDeviceClass.h create mode 100644 src/db/db/dbNet.cc create mode 100644 src/db/db/dbNet.h create mode 100644 src/db/db/dbNetlistUtils.h create mode 100644 src/db/db/dbPin.cc create mode 100644 src/db/db/dbPin.h create mode 100644 src/db/db/dbSubCircuit.cc create mode 100644 src/db/db/dbSubCircuit.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 9d22a4990..7a30f7331 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -153,7 +153,13 @@ SOURCES = \ gsiDeclDbHierNetworkProcessor.cc \ dbNetlistDeviceExtractorClasses.cc \ dbLayoutToNetlist.cc \ - gsiDeclDbLayoutToNetlist.cc + gsiDeclDbLayoutToNetlist.cc \ + dbCircuit.cc \ + dbDevice.cc \ + dbDeviceClass.cc \ + dbNet.cc \ + dbSubCircuit.cc \ + dbPin.cc HEADERS = \ dbArray.h \ @@ -270,7 +276,14 @@ HEADERS = \ dbNetlistExtractor.h \ dbNetlistDeviceExtractorClasses.h \ dbLayoutToNetlist.h \ - dbHierNetworkProcessor.h + dbHierNetworkProcessor.h \ + dbNetlistUtils.h \ + dbNet.h \ + dbCircuit.h \ + dbDevice.h \ + dbDeviceClass.h \ + dbPin.h \ + dbSubCircuit.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc new file mode 100644 index 000000000..e0c85f08c --- /dev/null +++ b/src/db/db/dbCircuit.cc @@ -0,0 +1,567 @@ + +/* + + 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 "dbCircuit.h" +#include "dbNetlist.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// Circuit class implementation + +Circuit::Circuit () + : mp_netlist (0), + m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), + m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), + m_index (0) +{ + m_devices.changed ().add (this, &Circuit::devices_changed); + m_nets.changed ().add (this, &Circuit::nets_changed); + m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed); +} + +Circuit::Circuit (const Circuit &other) + : mp_netlist (0), + m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), + m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), + m_index (0) +{ + operator= (other); + m_devices.changed ().add (this, &Circuit::devices_changed); + m_nets.changed ().add (this, &Circuit::nets_changed); + m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed); +} + +Circuit::~Circuit () +{ + m_devices.changed ().remove (this, &Circuit::devices_changed); + m_nets.changed ().remove (this, &Circuit::nets_changed); + m_subcircuits.changed ().remove (this, &Circuit::subcircuits_changed); + + // the default destructor will make the nets access "this" to unregister the + // objects - hence we have to do this explicitly. + m_nets.clear (); + m_subcircuits.clear (); + m_devices.clear (); +} + +Circuit &Circuit::operator= (const Circuit &other) +{ + if (this != &other) { + + clear (); + + m_name = other.m_name; + m_pins = other.m_pins; + + std::map device_table; + for (const_device_iterator i = other.begin_devices (); i != other.end_devices (); ++i) { + Device *d = new Device (*i); + device_table [i.operator-> ()] = d; + add_device (d); + } + + std::map sc_table; + for (const_subcircuit_iterator i = other.begin_subcircuits (); i != other.end_subcircuits (); ++i) { + SubCircuit *sc = new SubCircuit (*i); + sc_table [i.operator-> ()] = sc; + add_subcircuit (sc); + } + + for (const_net_iterator i = other.begin_nets (); i != other.end_nets (); ++i) { + + // translate the net + Net *n = new Net (); + n->set_cluster_id (i->cluster_id ()); + n->set_name (i->name ()); + add_net (n); + + for (Net::const_terminal_iterator p = i->begin_terminals (); p != i->end_terminals (); ++p) { + std::map::const_iterator m = device_table.find (p->device ()); + tl_assert (m != device_table.end ()); + n->add_terminal (NetTerminalRef (m->second, p->terminal_id ())); + } + + for (Net::const_pin_iterator p = i->begin_pins (); p != i->end_pins (); ++p) { + n->add_pin (NetPinRef (p->pin_id ())); + } + + for (Net::const_subcircuit_pin_iterator p = i->begin_subcircuit_pins (); p != i->end_subcircuit_pins (); ++p) { + std::map::const_iterator m = sc_table.find (p->subcircuit ()); + tl_assert (m != sc_table.end ()); + n->add_subcircuit_pin (NetSubcircuitPinRef (m->second, p->pin_id ())); + } + + } + + } + + return *this; +} + +void Circuit::set_netlist (Netlist *netlist) +{ + mp_netlist = netlist; +} + +const Pin *Circuit::pin_by_id (size_t id) const +{ + if (id >= m_pins.size ()) { + return 0; + } else { + return &m_pins [id]; + } +} + +const Pin *Circuit::pin_by_name (const std::string &name) const +{ + for (Circuit::const_pin_iterator p = begin_pins (); p != end_pins (); ++p) { + if (p->name () == name) { + return p.operator-> (); + } + } + return 0; +} + +void Circuit::devices_changed () +{ + m_device_by_id.invalidate (); + m_device_by_name.invalidate (); +} + +void Circuit::subcircuits_changed () +{ + m_subcircuit_by_id.invalidate (); + m_subcircuit_by_name.invalidate (); + + if (mp_netlist) { + mp_netlist->invalidate_topology (); + } +} + +void Circuit::nets_changed () +{ + m_net_by_cluster_id.invalidate (); + m_net_by_name.invalidate (); +} + +void Circuit::clear () +{ + m_name.clear (); + m_pins.clear (); + m_devices.clear (); + m_nets.clear (); + m_subcircuits.clear (); +} + +void Circuit::set_name (const std::string &name) +{ + m_name = name; + if (mp_netlist) { + mp_netlist->m_circuit_by_name.invalidate (); + } +} + +void Circuit::set_cell_index (const db::cell_index_type ci) +{ + m_cell_index = ci; + if (mp_netlist) { + mp_netlist->m_circuit_by_cell_index.invalidate (); + } +} + +Circuit::child_circuit_iterator Circuit::begin_children () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->child_circuits (this).begin (); +} + +Circuit::child_circuit_iterator Circuit::end_children () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->child_circuits (this).end (); +} + +Circuit::const_child_circuit_iterator Circuit::begin_children () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->child_circuits (const_cast (this))).begin (); +} + +Circuit::const_child_circuit_iterator Circuit::end_children () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->child_circuits (const_cast (this))).end (); +} + +Circuit::child_circuit_iterator Circuit::begin_parents () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->parent_circuits (this).begin (); +} + +Circuit::child_circuit_iterator Circuit::end_parents () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->parent_circuits (this).end (); +} + +Circuit::const_child_circuit_iterator Circuit::begin_parents () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->parent_circuits (const_cast (this))).begin (); +} + +Circuit::const_child_circuit_iterator Circuit::end_parents () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->parent_circuits (const_cast (this))).end (); +} + +const Pin &Circuit::add_pin (const std::string &name) +{ + m_pins.push_back (Pin (name)); + m_pins.back ().set_id (m_pins.size () - 1); + return m_pins.back (); +} + +void Circuit::add_net (Net *net) +{ + m_nets.push_back (net); + net->set_circuit (this); +} + +void Circuit::remove_net (Net *net) +{ + m_nets.erase (net); +} + +void Circuit::add_device (Device *device) +{ + device->set_circuit (this); + + size_t id = 0; + if (! m_devices.empty ()) { + tl_assert (m_devices.back () != 0); + id = m_devices.back ()->id (); + } + device->set_id (id + 1); + + m_devices.push_back (device); +} + +void Circuit::remove_device (Device *device) +{ + m_devices.erase (device); +} + +void Circuit::add_subcircuit (SubCircuit *subcircuit) +{ + subcircuit->set_circuit (this); + + size_t id = 0; + if (! m_subcircuits.empty ()) { + tl_assert (m_subcircuits.back () != 0); + id = m_subcircuits.back ()->id (); + } + subcircuit->set_id (id + 1); + + m_subcircuits.push_back (subcircuit); +} + +void Circuit::remove_subcircuit (SubCircuit *subcircuit) +{ + m_subcircuits.erase (subcircuit); +} + +void Circuit::register_ref (SubCircuit *r) +{ + m_refs.push_back (r); +} + +void Circuit::unregister_ref (SubCircuit *r) +{ + m_refs.erase (r); +} + +void Circuit::translate_circuits (const std::map &map) +{ + for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) { + std::map::const_iterator m = map.find (i->circuit_ref ()); + tl_assert (m != map.end ()); + i->set_circuit_ref (m->second); + } +} + +void Circuit::translate_device_classes (const std::map &map) +{ + for (device_iterator i = m_devices.begin (); i != m_devices.end (); ++i) { + std::map::const_iterator m = map.find (i->device_class ()); + tl_assert (m != map.end ()); + i->set_device_class (m->second); + } +} + +void Circuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter) +{ + if (m_pin_refs.size () < pin_id + 1) { + m_pin_refs.resize (pin_id + 1, Net::pin_iterator ()); + } + m_pin_refs [pin_id] = iter; +} + +const Net *Circuit::net_for_pin (size_t pin_id) const +{ + if (pin_id < m_pin_refs.size ()) { + Net::pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::pin_iterator ()) { + return p->net (); + } + } + return 0; +} + +void Circuit::connect_pin (size_t pin_id, Net *net) +{ + if (net_for_pin (pin_id) == net) { + return; + } + + if (pin_id < m_pin_refs.size ()) { + Net::pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::pin_iterator () && p->net ()) { + p->net ()->erase_pin (p); + } + m_pin_refs [pin_id] = Net::pin_iterator (); + } + + if (net) { + net->add_pin (NetPinRef (pin_id)); + } +} + +void Circuit::purge_nets () +{ + std::vector nets_to_be_purged; + for (net_iterator n = begin_nets (); n != end_nets (); ++n) { + if (n->is_floating ()) { + nets_to_be_purged.push_back (n.operator-> ()); + } + } + for (std::vector::const_iterator n = nets_to_be_purged.begin (); n != nets_to_be_purged.end (); ++n) { + delete *n; + } +} + +/** + * @brief Sanity check for device to be removed + */ +static void check_device_before_remove (db::Circuit *c, const db::Device *d) +{ + if (d->device_class () == 0) { + throw tl::Exception (tl::to_string (tr ("Internal error: No device class after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name ()); + } + const std::vector &pd = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (d->net_for_terminal (p->id ()) != 0) { + throw tl::Exception (tl::to_string (tr ("Internal error: Terminal still connected after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name () + ", terminal=" + p->name ()); + } + } +} + +bool Circuit::combine_parallel_devices (const db::DeviceClass &cls) +{ + typedef std::vector key_type; + std::map > combination_candidates; + + bool any = false; + + // identify the candidates for combination - all devices sharing the same nets + // are candidates for combination in parallel mode + for (device_iterator d = begin_devices (); d != end_devices (); ++d) { + + if (tl::id_of (d->device_class ()) != tl::id_of (&cls)) { + continue; + } + + key_type k; + const std::vector &terminals = cls.terminal_definitions (); + for (std::vector::const_iterator p = terminals.begin (); p != terminals.end (); ++p) { + const db::Net *n = d->net_for_terminal (p->id ()); + if (n) { + k.push_back (n); + } + } + + std::sort (k.begin (), k.end ()); + k.erase (std::unique (k.begin (), k.end ()), k.end ()); + combination_candidates[k].push_back (d.operator-> ()); + + } + + // actually combine the devices + for (std::map >::iterator cc = combination_candidates.begin (); cc != combination_candidates.end (); ++cc) { + + std::vector &cl = cc->second; + for (size_t i = 0; i < cl.size () - 1; ++i) { + for (size_t j = i + 1; j < cl.size (); ) { + if (cls.combine_devices (cl [i], cl [j])) { + check_device_before_remove (this, cl [j]); // sanity check + delete cl [j]; + cl.erase (cl.begin () + j); + any = true; + } else { + ++j; + } + } + } + + } + + return any; +} + +static std::pair attached_two_devices (db::Net &net, const db::DeviceClass &cls) +{ + if (net.begin_pins () != net.end_pins ()) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } + + db::Device *d1 = 0, *d2 = 0; + + Net::terminal_iterator p = net.begin_terminals (); + if (p == net.end_terminals () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } else { + d1 = p->device (); + } + + ++p; + if (p == net.end_terminals () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } else { + d2 = p->device (); + } + + ++p; + if (p != net.end_terminals () || d1 == d2 || !d1 || !d2) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } else { + return std::make_pair (d1, d2); + } +} + +template +static bool same_or_swapped (const std::pair &p1, const std::pair &p2) +{ + return (p1.first == p2.first && p1.second == p2.second) || (p1.first == p2.second && p1.second == p2.first); +} + +bool Circuit::combine_serial_devices(const db::DeviceClass &cls) +{ + bool any = false; + + for (net_iterator n = begin_nets (); n != end_nets (); ++n) { + + std::pair dd = attached_two_devices (*n, cls); + if (! dd.first) { + continue; + } + + // The net is an internal node: the devices attached to this internal node are + // combination candidates if the number of nets emerging from the attached device pair (not counting + // the internal node we just found) does not exceed the number of pins available for the + // new device. + + std::vector other_nets; + + const std::vector &terminals = cls.terminal_definitions (); + for (std::vector::const_iterator p = terminals.begin (); p != terminals.end (); ++p) { + db::Net *on; + on = dd.first->net_for_terminal (p->id ()); + if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) { + other_nets.push_back (on); + } + on = dd.second->net_for_terminal (p->id ()); + if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) { + other_nets.push_back (on); + } + } + + std::sort (other_nets.begin (), other_nets.end ()); + other_nets.erase (std::unique (other_nets.begin (), other_nets.end ()), other_nets.end ()); + + if (other_nets.size () <= cls.terminal_definitions().size ()) { + + // found a combination candidate + if (cls.combine_devices (dd.first, dd.second)) { + check_device_before_remove (this, dd.second); // sanity check + delete dd.second; + any = true; + } + + } + + } + + return any; +} + +void Circuit::combine_devices () +{ + tl_assert (netlist () != 0); + + for (Netlist::device_class_iterator dc = netlist ()->begin_device_classes (); dc != netlist ()->end_device_classes (); ++dc) { + + // repeat the combination step unless no combination happens - this is required to take care of combinations that arise after + // other combinations have been realized. + bool any = true; + while (any) { + + any = false; + + if (dc->supports_parallel_combination ()) { + if (combine_parallel_devices (*dc)) { + any = true; + } + } + if (dc->supports_serial_combination ()) { + if (combine_serial_devices (*dc)) { + any = true; + } + } + + } + + } +} + +} diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h new file mode 100644 index 000000000..98c01ecdb --- /dev/null +++ b/src/db/db/dbCircuit.h @@ -0,0 +1,634 @@ + +/* + + 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_dbCircuit +#define _HDR_dbCircuit + +#include "dbCommon.h" +#include "dbTypes.h" +#include "dbNet.h" +#include "dbDevice.h" +#include "dbPin.h" +#include "dbSubCircuit.h" +#include "dbNetlistUtils.h" + +#include "tlObject.h" +#include "tlObjectCollection.h" +#include "tlVector.h" +#include "gsiObject.h" + +namespace db +{ + +class Netlist; + +/** + * @brief A circuit + * + * A circuit is a list of nets, of subcircuit references and actual + * devices. + */ +class DB_PUBLIC Circuit + : public gsi::ObjectBase, public tl::Object +{ +public: + typedef tl::vector pin_list; + typedef pin_list::const_iterator const_pin_iterator; + typedef pin_list::iterator pin_iterator; + typedef tl::shared_collection device_list; + typedef device_list::const_iterator const_device_iterator; + typedef device_list::iterator device_iterator; + typedef tl::shared_collection net_list; + typedef net_list::const_iterator const_net_iterator; + typedef net_list::iterator net_iterator; + typedef tl::shared_collection subcircuit_list; + typedef subcircuit_list::const_iterator const_subcircuit_iterator; + typedef subcircuit_list::iterator subcircuit_iterator; + typedef tl::weak_collection::const_iterator const_refs_iterator; + typedef tl::weak_collection::iterator refs_iterator; + typedef tl::vector::const_iterator child_circuit_iterator; + typedef tl::vector::const_iterator const_child_circuit_iterator; + typedef tl::vector::const_iterator parent_circuit_iterator; + typedef tl::vector::const_iterator const_parent_circuit_iterator; + + /** + * @brief Constructor + * + * Creates an empty circuit. + */ + Circuit (); + + /** + * @brief Copy constructor + */ + Circuit (const Circuit &other); + + /** + * @brief Destructor + */ + ~Circuit (); + + /** + * @brief Assignment + */ + Circuit &operator= (const Circuit &other); + + /** + * @brief Gets the netlist the circuit lives in + * This pointer is 0 if the circuit is not part of a netlist. + */ + Netlist *netlist () + { + return mp_netlist; + } + + /** + * @brief Gets the netlist the circuit lives in (const version) + * This pointer is 0 if the circuit is not part of a netlist. + */ + const Netlist *netlist () const + { + return mp_netlist; + } + + /** + * @brief Clears the circuit + */ + void clear (); + + /** + * @brief Sets the name of the circuit + */ + void set_name (const std::string &name); + + /** + * @brief Gets the name of the circuit + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief The index of the circuit in the netlist + * CAUTION: this attribute is used for internal purposes and may not be valid always. + */ + size_t index () const + { + return m_index; + } + + /** + * @brief Sets the layout cell reference for this circuit + * + * The layout cell reference links a circuit to a layout cell. + */ + void set_cell_index (const db::cell_index_type ci); + + /** + * @brief Gets the layout cell index + */ + db::cell_index_type cell_index () const + { + return m_cell_index; + } + + /** + * @brief Gets the references to this circuit (begin, non-const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + refs_iterator begin_refs () + { + return m_refs.begin (); + } + + /** + * @brief Gets the references to this circuit (end, non-const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + refs_iterator end_refs () + { + return m_refs.end (); + } + + /** + * @brief Gets the references to this circuit (begin, const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + const_refs_iterator begin_refs () const + { + return m_refs.begin (); + } + + /** + * @brief Gets the child circuits iterator (begin) + * The child circuits are the circuits referenced by all subcircuits + * in the circuit. + */ + child_circuit_iterator begin_children (); + + /** + * @brief Gets the child circuits iterator (end) + */ + child_circuit_iterator end_children (); + + /** + * @brief Gets the child circuits iterator (begin, const version) + * The child circuits are the circuits referenced by all subcircuits + * in the circuit. + */ + const_child_circuit_iterator begin_children () const; + + /** + * @brief Gets the child circuits iterator (end, const version) + */ + const_child_circuit_iterator end_children () const; + + /** + * @brief Gets the parent circuits iterator (begin) + * The parents circuits are the circuits referencing this circuit via subcircuits. + */ + parent_circuit_iterator begin_parents (); + + /** + * @brief Gets the parent circuits iterator (end) + */ + parent_circuit_iterator end_parents (); + + /** + * @brief Gets the parent circuits iterator (begin, const version) + * The parents circuits are the circuits referencing this circuit via subcircuits. + */ + const_parent_circuit_iterator begin_parents () const; + + /** + * @brief Gets the parent circuits iterator (end, const version) + */ + const_parent_circuit_iterator end_parents () const; + + /** + * @brief Gets the references to this circuit (end, const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + const_refs_iterator end_refs () const + { + return m_refs.end (); + } + + /** + * @brief Adds a pin to this circuit + * The circuit takes over ownership of the object. + */ + const Pin &add_pin(const std::string &name); + + /** + * @brief Begin iterator for the pins of the circuit (non-const version) + */ + pin_iterator begin_pins () + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the circuit (non-const version) + */ + pin_iterator end_pins () + { + return m_pins.end (); + } + + /** + * @brief Gets the pin count + */ + size_t pin_count () const + { + return m_pins.size (); + } + + /** + * @brief Gets the pin by ID (the ID is basically the index) + */ + const Pin *pin_by_id (size_t id) const; + + /** + * @brief Gets the pin by name + * + * If there is no pin with that name, null is returned. + * NOTE: this is a linear search, so it's performance may not be good for many pins. + */ + const Pin *pin_by_name (const std::string &name) const; + + /** + * @brief Begin iterator for the pins of the circuit (const version) + */ + const_pin_iterator begin_pins () const + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the circuit (const version) + */ + const_pin_iterator end_pins () const + { + return m_pins.end (); + } + + /** + * @brief Adds a net to this circuit + * + * The circuit takes over ownership of the object. + */ + void add_net (Net *net); + + /** + * @brief Deletes a net from the circuit + */ + void remove_net (Net *net); + + /** + * @brief Begin iterator for the nets of the circuit (non-const version) + */ + net_iterator begin_nets () + { + return m_nets.begin (); + } + + /** + * @brief End iterator for the nets of the circuit (non-const version) + */ + net_iterator end_nets () + { + return m_nets.end (); + } + + /** + * @brief Begin iterator for the nets of the circuit (const version) + */ + const_net_iterator begin_nets () const + { + return m_nets.begin (); + } + + /** + * @brief End iterator for the nets of the circuit (const version) + */ + const_net_iterator end_nets () const + { + return m_nets.end (); + } + + /** + * @brief Gets the net from a given cluster ID (const version) + * + * If the cluster ID is not valid, null is returned. + */ + const Net *net_by_cluster_id (size_t cluster_id) const + { + return (const_cast (this)->net_by_cluster_id (cluster_id)); + } + + /** + * @brief Gets the net from a given cluster ID (non-const version) + * + * If the cluster ID is not valid, null is returned. + */ + Net *net_by_cluster_id (size_t cluster_id) + { + return m_net_by_cluster_id.object_by (cluster_id); + } + + /** + * @brief Gets the net from a given name (const version) + * + * If the name is not valid, null is returned. + */ + const Net *net_by_name (const std::string &name) const + { + return (const_cast (this)->net_by_name (name)); + } + + /** + * @brief Gets the net from a given name (non-const version) + * + * If the name is not valid, null is returned. + */ + Net *net_by_name (const std::string &name) + { + return m_net_by_name.object_by (name); + } + + /** + * @brief Adds a device to this circuit + * + * The circuit takes over ownership of the object. + */ + void add_device (Device *device); + + /** + * @brief Deletes a device from the circuit + */ + void remove_device (Device *device); + + /** + * @brief Gets the device from a given ID (const version) + * + * If the ID is not valid, null is returned. + */ + const Device *device_by_id (size_t id) const + { + return (const_cast (this)->device_by_id (id)); + } + + /** + * @brief Gets the device from a given ID (non-const version) + * + * If the ID is not valid, null is returned. + */ + Device *device_by_id (size_t id) + { + return m_device_by_id.object_by (id); + } + + /** + * @brief Gets the device from a given name (const version) + * + * If the name is not valid, null is returned. + */ + const Device *device_by_name (const std::string &name) const + { + return (const_cast (this)->device_by_name (name)); + } + + /** + * @brief Gets the device from a given name (non-const version) + * + * If the name is not valid, null is returned. + */ + Device *device_by_name (const std::string &name) + { + return m_device_by_name.object_by (name); + } + + /** + * @brief Begin iterator for the devices of the circuit (non-const version) + */ + device_iterator begin_devices () + { + return m_devices.begin (); + } + + /** + * @brief End iterator for the devices of the circuit (non-const version) + */ + device_iterator end_devices () + { + return m_devices.end (); + } + + /** + * @brief Begin iterator for the devices of the circuit (const version) + */ + const_device_iterator begin_devices () const + { + return m_devices.begin (); + } + + /** + * @brief End iterator for the devices of the circuit (const version) + */ + const_device_iterator end_devices () const + { + return m_devices.end (); + } + + /** + * @brief Adds a subcircuit to this circuit + * + * The circuit takes over ownership of the object. + */ + void add_subcircuit (SubCircuit *subcircuit); + + /** + * @brief Deletes a subcircuit from the circuit + */ + void remove_subcircuit (SubCircuit *subcircuit); + + /** + * @brief Gets the subcircuit from a given ID (const version) + * + * If the ID is not valid, null is returned. + */ + const SubCircuit *subcircuit_by_id (size_t id) const + { + return (const_cast (this)->subcircuit_by_id (id)); + } + + /** + * @brief Gets the subcircuit from a given ID (non-const version) + * + * If the ID is not valid, null is returned. + */ + SubCircuit *subcircuit_by_id (size_t id) + { + return m_subcircuit_by_id.object_by (id); + } + + /** + * @brief Gets the subcircuit from a given name (const version) + * + * If the name is not valid, null is returned. + */ + const SubCircuit *subcircuit_by_name (const std::string &name) const + { + return (const_cast (this)->subcircuit_by_name (name)); + } + + /** + * @brief Gets the subcircuit from a given name (non-const version) + * + * If the name is not valid, null is returned. + */ + SubCircuit *subcircuit_by_name (const std::string &name) + { + return m_subcircuit_by_name.object_by (name); + } + + /** + * @brief Begin iterator for the subcircuits of the circuit (non-const version) + */ + subcircuit_iterator begin_subcircuits () + { + return m_subcircuits.begin (); + } + + /** + * @brief End iterator for the subcircuits of the circuit (non-const version) + */ + subcircuit_iterator end_subcircuits () + { + return m_subcircuits.end (); + } + + /** + * @brief Begin iterator for the subcircuits of the circuit (const version) + */ + const_subcircuit_iterator begin_subcircuits () const + { + return m_subcircuits.begin (); + } + + /** + * @brief End iterator for the subcircuits of the circuit (const version) + */ + const_subcircuit_iterator end_subcircuits () const + { + return m_subcircuits.end (); + } + + /** + * @brief Gets the connected net for a pin with the given id + * + * Returns 0 if the pin is not connected to a net. + */ + const Net *net_for_pin (size_t pin_id) const; + + /** + * @brief Gets the connected net for a pin with the given id (non-const version) + * + * Returns 0 if the pin is not connected to a net. + */ + Net *net_for_pin (size_t pin_id) + { + return const_cast (((const Circuit *) this)->net_for_pin (pin_id)); + } + + /** + * @brief Connects the given pin to the given net + * If the net is 0 the pin is disconnected. + * If non-null, a NetPinRef object will be inserted into the + * net and connected with the given pin. + */ + void connect_pin (size_t pin_id, Net *net); + + /** + * @brief Purge unused nets + * + * This method will purge all nets which return "floating". + */ + void purge_nets (); + + /** + * @brief Combine devices + * + * This method will combine devices that can be combined according + * to their device classes "combine_devices" method. + */ + void combine_devices (); + +private: + friend class Netlist; + friend class Net; + friend class SubCircuit; + friend class Device; + + std::string m_name; + db::cell_index_type m_cell_index; + net_list m_nets; + pin_list m_pins; + device_list m_devices; + subcircuit_list m_subcircuits; + Netlist *mp_netlist; + std::vector m_pin_refs; + object_by_attr > m_device_by_id; + object_by_attr > m_subcircuit_by_id; + object_by_attr > m_net_by_cluster_id; + object_by_attr > m_device_by_name; + object_by_attr > m_subcircuit_by_name; + object_by_attr > m_net_by_name; + tl::weak_collection m_refs; + size_t m_index; + + void set_index (size_t index) + { + m_index = index; + } + + void set_pin_ref_for_pin (size_t ppin_id, Net::pin_iterator iter); + + void register_ref (SubCircuit *sc); + void unregister_ref (SubCircuit *sc); + + void translate_circuits (const std::map &map); + void translate_device_classes (const std::map &map); + void set_netlist (Netlist *netlist); + bool combine_parallel_devices (const db::DeviceClass &cls); + bool combine_serial_devices (const db::DeviceClass &cls); + + void devices_changed (); + void subcircuits_changed (); + void nets_changed (); +}; + +} + +#endif diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc new file mode 100644 index 000000000..dd9b8d8cf --- /dev/null +++ b/src/db/db/dbDevice.cc @@ -0,0 +1,167 @@ + +/* + + 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 "dbDevice.h" +#include "dbCircuit.h" +#include "dbDeviceClass.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// Device class implementation + +Device::Device () + : mp_device_class (0), m_id (0), mp_circuit (0) +{ + // .. nothing yet .. +} + +Device::~Device () +{ + for (std::vector::const_iterator t = m_terminal_refs.begin (); t != m_terminal_refs.end (); ++t) { + if (*t != Net::terminal_iterator () && (*t)->net ()) { + (*t)->net ()->erase_terminal (*t); + } + } +} + +Device::Device (DeviceClass *device_class, const std::string &name) + : mp_device_class (device_class), m_name (name), m_id (0), mp_circuit (0) +{ + // .. nothing yet .. +} + +Device::Device (const Device &other) + : mp_device_class (0), m_id (0), mp_circuit (0) +{ + operator= (other); +} + +Device &Device::operator= (const Device &other) +{ + if (this != &other) { + m_name = other.m_name; + mp_device_class = other.mp_device_class; + } + return *this; +} + +void Device::set_circuit (Circuit *circuit) +{ + mp_circuit = circuit; +} + +void Device::set_name (const std::string &n) +{ + m_name = n; + if (mp_circuit) { + mp_circuit->m_device_by_name.invalidate (); + } +} + +void Device::set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter) +{ + if (m_terminal_refs.size () < terminal_id + 1) { + m_terminal_refs.resize (terminal_id + 1, Net::terminal_iterator ()); + } + m_terminal_refs [terminal_id] = iter; +} + +const Net *Device::net_for_terminal (size_t terminal_id) const +{ + if (terminal_id < m_terminal_refs.size ()) { + Net::terminal_iterator p = m_terminal_refs [terminal_id]; + if (p != Net::terminal_iterator ()) { + return p->net (); + } + } + return 0; +} + +void Device::connect_terminal (size_t terminal_id, Net *net) +{ + if (net_for_terminal (terminal_id) == net) { + return; + } + + if (terminal_id < m_terminal_refs.size ()) { + Net::terminal_iterator p = m_terminal_refs [terminal_id]; + if (p != Net::terminal_iterator () && p->net ()) { + p->net ()->erase_terminal (p); + } + m_terminal_refs [terminal_id] = Net::terminal_iterator (); + } + + if (net) { + net->add_terminal (NetTerminalRef (this, terminal_id)); + } +} + +double Device::parameter_value (size_t param_id) const +{ + if (m_parameters.size () > param_id) { + return m_parameters [param_id]; + } else if (mp_device_class) { + const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (param_id); + if (pd) { + return pd->default_value (); + } + } + return 0.0; +} + +void Device::set_parameter_value (size_t param_id, double v) +{ + if (m_parameters.size () <= param_id) { + + // resize the parameter vector with default values + size_t from_size = m_parameters.size (); + m_parameters.resize (param_id + 1, 0.0); + + if (mp_device_class) { + for (size_t n = from_size; n < param_id; ++n) { + const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (n); + if (pd) { + m_parameters [n] = pd->default_value (); + } + } + } + + } + + m_parameters [param_id] = v; +} + +double Device::parameter_value (const std::string &name) const +{ + return device_class () ? parameter_value (device_class ()->parameter_id_for_name (name)) : 0.0; +} + +void Device::set_parameter_value (const std::string &name, double v) +{ + if (device_class ()) { + set_parameter_value (device_class ()->parameter_id_for_name (name), v); + } +} + +} diff --git a/src/db/db/dbDevice.h b/src/db/db/dbDevice.h new file mode 100644 index 000000000..c918cbe35 --- /dev/null +++ b/src/db/db/dbDevice.h @@ -0,0 +1,217 @@ + +/* + + 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_dbDevice +#define _HDR_dbDevice + +#include "dbCommon.h" +#include "dbNet.h" + +#include "tlObject.h" + +#include + +namespace db +{ + +class Circuit; +class DeviceClass; + +/** + * @brief An actual device + * + * This class represents the incarnation of a specific device. + * The device has a class which specifies a type. This class + * is intended for subclassing. + * A specific device subclass is supposed to correspond to + * a specific device class. + */ +class DB_PUBLIC Device + : public tl::Object +{ +public: + typedef std::vector > global_connections; + typedef global_connections::const_iterator global_connections_iterator; + + /** + * @brief Default constructor + */ + Device (); + + /** + * @brief The constructor + */ + Device (DeviceClass *device_class, const std::string &name = std::string ()); + + /** + * @brief Copy constructor + */ + Device (const Device &other); + + /** + * @brief Assignment + */ + Device &operator= (const Device &other); + + /** + * @brief Destructor + */ + ~Device (); + + /** + * @brief Gets the device class + */ + const DeviceClass *device_class () const + { + return mp_device_class; + } + + /** + * @brief Gets the device ID + * The ID is a unique integer which identifies the device. + * It can be used to retrieve the device from the circuit using Circuit::device_by_id. + * When assigned, the device ID is not 0. + */ + size_t id () const + { + return m_id; + } + + /** + * @brief Gets the circuit the device lives in (const version) + * This pointer is 0 if the device isn't added to a circuit + */ + const Circuit *circuit () const + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the device lives in (non-const version) + * This pointer is 0 if the device isn't added to a circuit + */ + Circuit *circuit () + { + return mp_circuit; + } + + /** + * @brief Sets the name + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Gets the net attached to a specific terminal + * Returns 0 if no net is attached. + */ + const Net *net_for_terminal (size_t terminal_id) const; + + /** + * @brief Gets the net attached to a specific terminal (non-const version) + * Returns 0 if no net is attached. + */ + Net *net_for_terminal (size_t terminal_id) + { + return const_cast (((const Device *) this)->net_for_terminal (terminal_id)); + } + + /** + * @brief Connects the given terminal to the given net + * If the net is 0 the terminal is disconnected. + * If non-null, a NetTerminalRef object will be inserted into the + * net and connected with the given terminal. + * If the terminal is connected to a global net, it will be + * disconnected from there. + */ + void connect_terminal (size_t terminal_id, Net *net); + + /** + * @brief Gets the value for the parameter with the given ID + */ + double parameter_value (size_t param_id) const; + + /** + * @brief Sets the value for the parameter with the given ID + */ + void set_parameter_value (size_t param_id, double v); + + /** + * @brief Gets the value for the parameter with the given name + * If the name is not valid, an exception is thrown. + */ + double parameter_value (const std::string &name) const; + + /** + * @brief Sets the value for the parameter with the given name + * If the name is not valid, an exception is thrown. + */ + void set_parameter_value (const std::string &name, double v); + +private: + friend class Circuit; + friend class Net; + + DeviceClass *mp_device_class; + std::string m_name; + std::vector m_terminal_refs; + std::vector m_parameters; + size_t m_id; + Circuit *mp_circuit; + + /** + * @brief Sets the terminal reference for a specific terminal + */ + void set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter); + + /** + * @brief Sets the device class + */ + void set_device_class (DeviceClass *dc) + { + mp_device_class = dc; + } + + /** + * @brief Sets the device ID + */ + void set_id (size_t id) + { + m_id = id; + } + + /** + * @brief Sets the circuit + */ + void set_circuit (Circuit *circuit); +}; + +} + +#endif diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc new file mode 100644 index 000000000..46a2c1a80 --- /dev/null +++ b/src/db/db/dbDeviceClass.cc @@ -0,0 +1,139 @@ + +/* + + 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 "dbDeviceClass.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// DeviceClass class implementation + +DeviceClass::DeviceClass () + : mp_netlist (0) +{ + // .. nothing yet .. +} + +DeviceClass::DeviceClass (const DeviceClass &other) + : mp_netlist (0) +{ + operator= (other); +} + +DeviceClass &DeviceClass::operator= (const DeviceClass &other) +{ + if (this != &other) { + m_terminal_definitions = other.m_terminal_definitions; + m_name = other.m_name; + m_description = other.m_description; + } + return *this; +} + +const DeviceTerminalDefinition &DeviceClass::add_terminal_definition (const DeviceTerminalDefinition &pd) +{ + m_terminal_definitions.push_back (pd); + m_terminal_definitions.back ().set_id (m_terminal_definitions.size () - 1); + return m_terminal_definitions.back (); +} + +void DeviceClass::clear_terminal_definitions () +{ + m_terminal_definitions.clear (); +} + +const DeviceTerminalDefinition *DeviceClass::terminal_definition (size_t id) const +{ + if (id < m_terminal_definitions.size ()) { + return & m_terminal_definitions [id]; + } else { + return 0; + } +} + +const DeviceParameterDefinition &DeviceClass::add_parameter_definition (const DeviceParameterDefinition &pd) +{ + m_parameter_definitions.push_back (pd); + m_parameter_definitions.back ().set_id (m_parameter_definitions.size () - 1); + return m_parameter_definitions.back (); +} + +void DeviceClass::clear_parameter_definitions () +{ + m_parameter_definitions.clear (); +} + +const DeviceParameterDefinition *DeviceClass::parameter_definition (size_t id) const +{ + if (id < m_parameter_definitions.size ()) { + return & m_parameter_definitions [id]; + } else { + return 0; + } +} + +bool DeviceClass::has_parameter_with_name (const std::string &name) const +{ + const std::vector &pd = parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->name () == name) { + return true; + } + } + return false; +} + +size_t DeviceClass::parameter_id_for_name (const std::string &name) const +{ + const std::vector &pd = parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->name () == name) { + return i->id (); + } + } + throw tl::Exception (tl::to_string (tr ("Invalid parameter name")) + ": '" + name + "'"); +} + +bool DeviceClass::has_terminal_with_name (const std::string &name) const +{ + const std::vector &td = terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + if (i->name () == name) { + return true; + } + } + return false; +} + +size_t DeviceClass::terminal_id_for_name (const std::string &name) const +{ + const std::vector &td = terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + if (i->name () == name) { + return i->id (); + } + } + throw tl::Exception (tl::to_string (tr ("Invalid terminal name")) + ": '" + name + "'"); +} + +} diff --git a/src/db/db/dbDeviceClass.h b/src/db/db/dbDeviceClass.h new file mode 100644 index 000000000..8c4b34aca --- /dev/null +++ b/src/db/db/dbDeviceClass.h @@ -0,0 +1,422 @@ + +/* + + 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_dbDeviceClass +#define _HDR_dbDeviceClass + +#include "dbCommon.h" + +#include "gsiObject.h" +#include "tlObject.h" +#include "tlUniqueId.h" + +#include +#include + +namespace db +{ + +class Netlist; +class Device; + +/** + * @brief A device terminal definition + */ +class DB_PUBLIC DeviceTerminalDefinition +{ +public: + /** + * @brief Creates an empty device terminal definition + */ + DeviceTerminalDefinition () + : m_name (), m_description (), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Creates a device terminal definition with the given name and description + */ + DeviceTerminalDefinition (const std::string &name, const std::string &description) + : m_name (name), m_description (description), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Gets the terminal name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the terminal name + */ + void set_name (const std::string &n) + { + m_name = n; + } + + /** + * @brief Gets the terminal description + */ + const std::string &description () const + { + return m_description; + } + + /** + * @brief Sets the terminal description + */ + void set_description (const std::string &d) + { + m_description = d; + } + + /** + * @brief Gets the terminal ID + */ + size_t id () const + { + return m_id; + } + +private: + friend class DeviceClass; + + std::string m_name, m_description; + size_t m_id; + + void set_id (size_t id) + { + m_id = id; + } +}; + +/** + * @brief A device parameter definition + */ +class DB_PUBLIC DeviceParameterDefinition +{ +public: + /** + * @brief Creates an empty device parameter definition + */ + DeviceParameterDefinition () + : m_name (), m_description (), m_default_value (0.0), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Creates a device parameter definition with the given name and description + */ + DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0) + : m_name (name), m_description (description), m_default_value (default_value), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Gets the parameter name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the parameter name + */ + void set_name (const std::string &n) + { + m_name = n; + } + + /** + * @brief Gets the parameter description + */ + const std::string &description () const + { + return m_description; + } + + /** + * @brief Sets the parameter description + */ + void set_description (const std::string &d) + { + m_description = d; + } + + /** + * @brief Gets the parameter default value + */ + double default_value () const + { + return m_default_value; + } + + /** + * @brief Sets the parameter description + */ + void set_default_value (double d) + { + m_default_value = d; + } + + /** + * @brief Gets the parameter ID + */ + size_t id () const + { + return m_id; + } + +private: + friend class DeviceClass; + + std::string m_name, m_description; + double m_default_value; + size_t m_id; + + void set_id (size_t id) + { + m_id = id; + } +}; + +/** + * @brief A device class + * + * A device class describes a type of device. + */ +class DB_PUBLIC DeviceClass + : public gsi::ObjectBase, public tl::Object, public tl::UniqueId +{ +public: + typedef size_t terminal_id_type; + + /** + * @brief Constructor + * + * Creates an empty circuit. + */ + DeviceClass (); + + /** + * @brief Copy constructor + * NOTE: do not use this copy constructor as the device class + * is intended to subclassing. + */ + DeviceClass (const DeviceClass &other); + + /** + * @brief Assignment + * NOTE: do not use this copy constructor as the device class + * is intended to subclassing. + */ + DeviceClass &operator= (const DeviceClass &other); + + /** + * @brief Gets the netlist the device class lives in + */ + db::Netlist *netlist () + { + return mp_netlist; + } + + /** + * @brief Gets the netlist the device class lives in (const version) + */ + const db::Netlist *netlist () const + { + return mp_netlist; + } + + /** + * @brief Gets the name of the device class + * + * The name is a formal name which identifies the class. + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the device name + */ + void set_name (const std::string &n) + { + m_name = n; + } + + /** + * @brief Gets the description text for the device class + * + * The description text is a human-readable text that + * identifies the device class. + */ + const std::string &description () const + { + return m_description; + } + + /** + * @brief Sets the description text + */ + void set_description (const std::string &d) + { + m_description = d; + } + + /** + * @brief Gets the terminal definitions + * + * The terminal definitions indicate what terminals the device offers. + * The number of terminals is constant per class. The index of the terminal + * is used as an ID of the terminal, hence the order must be static. + */ + const std::vector &terminal_definitions () const + { + return m_terminal_definitions; + } + + /** + * @brief Adds a terminal definition + */ + const DeviceTerminalDefinition &add_terminal_definition (const DeviceTerminalDefinition &pd); + + /** + * @brief Clears the terminal definition + */ + void clear_terminal_definitions (); + + /** + * @brief Gets the terminal definition from the ID + */ + const DeviceTerminalDefinition *terminal_definition (size_t id) const; + + /** + * @brief Gets the parameter definitions + */ + const std::vector ¶meter_definitions () const + { + return m_parameter_definitions; + } + + /** + * @brief Adds a parameter definition + */ + const DeviceParameterDefinition &add_parameter_definition (const DeviceParameterDefinition &pd); + + /** + * @brief Clears the parameter definition + */ + void clear_parameter_definitions (); + + /** + * @brief Gets the parameter definition from the ID + */ + const DeviceParameterDefinition *parameter_definition (size_t id) const; + + /** + * @brief Returns true, if the device has a parameter with the given name + */ + bool has_parameter_with_name (const std::string &name) const; + + /** + * @brief Returns the parameter ID for the parameter with the given name + * If the name is invalid, an exception is thrown. + */ + size_t parameter_id_for_name (const std::string &name) const; + + /** + * @brief Returns true, if the device has a terminal with the given name + */ + bool has_terminal_with_name (const std::string &name) const; + + /** + * @brief Returns the parameter ID for the terminal with the given name + * If the name is invalid, an exception is thrown. + */ + size_t terminal_id_for_name (const std::string &name) const; + + /** + * @brief Clears the circuit + */ + virtual DeviceClass *clone () const + { + return new DeviceClass (*this); + } + + /** + * @brief Combines two devices + * + * This method shall test, whether the two devices can be combined. Both devices + * are guaranteed to share the same device class (this). + * If they cannot be combined, this method shall do nothing and return false. + * If they can be combined, this method shall reconnect the nets of the first + * device and entirely disconnect the nets of the second device. + * The second device will be deleted afterwards. + */ + virtual bool combine_devices (db::Device * /*a*/, db::Device * /*b*/) const + { + return false; + } + + /** + * @brief Returns true if the device class supports device combination in parallel mode + */ + virtual bool supports_parallel_combination () const + { + return false; + } + + /** + * @brief Returns true if the device class supports device combination in serial mode + */ + virtual bool supports_serial_combination () const + { + return false; + } + +private: + friend class Netlist; + + std::string m_name, m_description; + std::vector m_terminal_definitions; + std::vector m_parameter_definitions; + db::Netlist *mp_netlist; + + void set_netlist (db::Netlist *nl) + { + mp_netlist = nl; + } +}; + +} + +#endif diff --git a/src/db/db/dbNet.cc b/src/db/db/dbNet.cc new file mode 100644 index 000000000..20733d442 --- /dev/null +++ b/src/db/db/dbNet.cc @@ -0,0 +1,324 @@ + +/* + + 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 "dbNet.h" +#include "dbDevice.h" +#include "dbDeviceClass.h" +#include "dbCircuit.h" +#include "dbSubCircuit.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// NetTerminalRef class implementation + +NetTerminalRef::NetTerminalRef () + : m_terminal_id (0), mp_device (0), mp_net (0) +{ + // .. nothing yet .. +} + +NetTerminalRef::NetTerminalRef (Device *device, size_t terminal_id) + : m_terminal_id (terminal_id), mp_device (device), mp_net (0) +{ + // .. nothing yet .. +} + +NetTerminalRef::NetTerminalRef (const NetTerminalRef &other) + : m_terminal_id (other.m_terminal_id), mp_device (other.mp_device), mp_net (0) +{ + // .. nothing yet .. +} + +NetTerminalRef &NetTerminalRef::operator= (const NetTerminalRef &other) +{ + if (this != &other) { + m_terminal_id = other.m_terminal_id; + mp_device = other.mp_device; + } + return *this; +} + +const DeviceTerminalDefinition * +NetTerminalRef::terminal_def () const +{ + const DeviceClass *dc = device_class (); + if (dc) { + return dc->terminal_definition (m_terminal_id); + } else { + return 0; + } +} + +const DeviceClass * +NetTerminalRef::device_class () const +{ + return mp_device ? mp_device->device_class () : 0; +} + +// -------------------------------------------------------------------------------- +// NetPinRef class implementation + +NetPinRef::NetPinRef () + : m_pin_id (0), mp_net (0) +{ + // .. nothing yet .. +} + +NetPinRef::NetPinRef (size_t pin_id) + : m_pin_id (pin_id), mp_net (0) +{ + // .. nothing yet .. +} + +NetPinRef::NetPinRef (const NetPinRef &other) + : m_pin_id (other.m_pin_id), mp_net (0) +{ + // .. nothing yet .. +} + +NetPinRef &NetPinRef::operator= (const NetPinRef &other) +{ + if (this != &other) { + m_pin_id = other.m_pin_id; + } + return *this; +} + +const Pin *NetPinRef::pin () const +{ + if (mp_net && mp_net->circuit ()) { + return mp_net->circuit ()->pin_by_id (m_pin_id); + } + return 0; +} + +// -------------------------------------------------------------------------------- +// NetSubcircuitPinRef class implementation + +NetSubcircuitPinRef::NetSubcircuitPinRef () + : m_pin_id (0), mp_subcircuit (0), mp_net (0) +{ + // .. nothing yet .. +} + +NetSubcircuitPinRef::NetSubcircuitPinRef (SubCircuit *circuit, size_t pin_id) + : m_pin_id (pin_id), mp_subcircuit (circuit), mp_net (0) +{ + // .. nothing yet .. +} + +NetSubcircuitPinRef::NetSubcircuitPinRef (const NetSubcircuitPinRef &other) + : m_pin_id (other.m_pin_id), mp_subcircuit (other.mp_subcircuit), mp_net (0) +{ + // .. nothing yet .. +} + +NetSubcircuitPinRef &NetSubcircuitPinRef::operator= (const NetSubcircuitPinRef &other) +{ + if (this != &other) { + m_pin_id = other.m_pin_id; + mp_subcircuit = other.mp_subcircuit; + } + return *this; +} + +const Pin *NetSubcircuitPinRef::pin () const +{ + if (mp_subcircuit && mp_subcircuit->circuit_ref ()) { + return mp_subcircuit->circuit_ref ()->pin_by_id (m_pin_id); + } + return 0; +} + +// -------------------------------------------------------------------------------- +// Net class implementation + +Net::Net () + : m_cluster_id (0), mp_circuit (0) +{ + // .. nothing yet .. +} + +Net::Net (const std::string &name) + : m_cluster_id (0), mp_circuit (0) +{ + m_name = name; +} + +Net::Net (const Net &other) + : m_cluster_id (0), mp_circuit (0) +{ + operator= (other); +} + +Net &Net::operator= (const Net &other) +{ + if (this != &other) { + + clear (); + + m_name = other.m_name; + m_cluster_id = other.m_cluster_id; + + for (const_subcircuit_pin_iterator i = other.begin_subcircuit_pins (); i != other.end_subcircuit_pins (); ++i) { + add_subcircuit_pin (*i); + } + + for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { + add_pin (*i); + } + + for (const_terminal_iterator i = other.begin_terminals (); i != other.end_terminals (); ++i) { + add_terminal (*i); + } + + } + return *this; +} + +Net::~Net () +{ + clear (); +} + +void Net::clear () +{ + m_name.clear (); + m_cluster_id = 0; + + while (! m_terminals.empty ()) { + erase_terminal (begin_terminals ()); + } + + while (! m_pins.empty ()) { + erase_pin (begin_pins ()); + } + + while (! m_subcircuit_pins.empty ()) { + erase_subcircuit_pin (begin_subcircuit_pins ()); + } +} + +void Net::set_name (const std::string &name) +{ + m_name = name; + if (mp_circuit) { + mp_circuit->m_net_by_name.invalidate (); + } +} + +std::string Net::qname () const +{ + if (circuit ()) { + return circuit ()->name () + ":" + expanded_name (); + } else { + return expanded_name (); + } +} + +std::string Net::expanded_name () const +{ + if (name ().empty ()) { + if (cluster_id () > std::numeric_limits::max () / 2) { + // avoid printing huge ID numbers for internal cluster IDs + return "$I" + tl::to_string ((std::numeric_limits::max () - cluster_id ()) + 1); + } else { + return "$" + tl::to_string (cluster_id ()); + } + } else { + return name (); + } +} + +void Net::set_cluster_id (size_t ci) +{ + m_cluster_id = ci; + if (mp_circuit) { + mp_circuit->m_net_by_cluster_id.invalidate (); + } +} + +void Net::add_pin (const NetPinRef &pin) +{ + m_pins.push_back (pin); + NetPinRef &new_pin = m_pins.back (); + new_pin.set_net (this); + + if (mp_circuit) { + mp_circuit->set_pin_ref_for_pin (new_pin.pin_id (), --m_pins.end ()); + } +} + +void Net::add_subcircuit_pin (const NetSubcircuitPinRef &pin) +{ + m_subcircuit_pins.push_back (pin); + NetSubcircuitPinRef &new_pin = m_subcircuit_pins.back (); + new_pin.set_net (this); + + tl_assert (pin.subcircuit () != 0); + new_pin.subcircuit ()->set_pin_ref_for_pin (new_pin.pin_id (), --m_subcircuit_pins.end ()); +} + +void Net::erase_pin (pin_iterator iter) +{ + if (mp_circuit) { + mp_circuit->set_pin_ref_for_pin (iter->pin_id (), pin_iterator ()); + } + m_pins.erase (iter); +} + +void Net::erase_subcircuit_pin (subcircuit_pin_iterator iter) +{ + if (iter->subcircuit ()) { + iter->subcircuit ()->set_pin_ref_for_pin (iter->pin_id (), subcircuit_pin_iterator ()); + } + m_subcircuit_pins.erase (iter); +} + +void Net::add_terminal (const NetTerminalRef &terminal) +{ + if (! terminal.device ()) { + return; + } + + m_terminals.push_back (terminal); + NetTerminalRef &new_terminal = m_terminals.back (); + new_terminal.set_net (this); + new_terminal.device ()->set_terminal_ref_for_terminal (new_terminal.terminal_id (), --m_terminals.end ()); +} + +void Net::erase_terminal (terminal_iterator iter) +{ + if (iter->device ()) { + iter->device ()->set_terminal_ref_for_terminal (iter->terminal_id (), terminal_iterator ()); + } + m_terminals.erase (iter); +} + +void Net::set_circuit (Circuit *circuit) +{ + mp_circuit = circuit; +} + +} diff --git a/src/db/db/dbNet.h b/src/db/db/dbNet.h new file mode 100644 index 000000000..6baa895b9 --- /dev/null +++ b/src/db/db/dbNet.h @@ -0,0 +1,651 @@ + +/* + + 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_dbNet +#define _HDR_dbNet + +#include "dbCommon.h" + +#include "tlObject.h" + +#include +#include + +namespace db +{ + +class Device; +class Net; +class SubCircuit; +class Circuit; +class DeviceTerminalDefinition; +class DeviceClass; +class Pin; + +/** + * @brief A reference to a terminal of a device + * + * A terminal must always refer to a device inside the current circuit. + */ +class DB_PUBLIC NetTerminalRef +{ +public: + /** + * @brief Default constructor + */ + NetTerminalRef (); + + /** + * @brief Creates a pin reference to the given pin of the current circuit + */ + NetTerminalRef (Device *device, size_t terminal_id); + + /** + * @brief Copy constructor + */ + NetTerminalRef (const NetTerminalRef &other); + + /** + * @brief Assignment + */ + NetTerminalRef &operator= (const NetTerminalRef &other); + + /** + * @brief Comparison + */ + bool operator< (const NetTerminalRef &other) const + { + if (mp_device != other.mp_device) { + return mp_device < other.mp_device; + } + return m_terminal_id < other.m_terminal_id; + } + + /** + * @brief Equality + */ + bool operator== (const NetTerminalRef &other) const + { + return (mp_device == other.mp_device && m_terminal_id == other.m_terminal_id); + } + + /** + * @brief Gets the device reference + */ + Device *device () + { + return mp_device; + } + + /** + * @brief Gets the device reference (const version) + */ + const Device *device () const + { + return mp_device; + } + + /** + * @brief Gets the terminal index + */ + size_t terminal_id () const + { + return m_terminal_id; + } + + /** + * @brief Gets the terminal definition + * + * Returns 0 if the terminal is not a valid terminal reference. + */ + const DeviceTerminalDefinition *terminal_def () const; + + /** + * @brief Returns the device class + */ + const DeviceClass *device_class () const; + + /** + * @brief Gets the net the terminal lives in + */ + Net *net () + { + return mp_net; + } + + /** + * @brief Gets the net the terminal lives in (const version) + */ + const Net *net () const + { + return mp_net; + } + +private: + friend class Net; + + size_t m_terminal_id; + Device *mp_device; + Net *mp_net; + + /** + * @brief Sets the net the terminal lives in + */ + void set_net (Net *net) + { + mp_net = net; + } +}; + +/** + * @brief A reference to a pin inside a net + * + * This object describes a connection to an outgoing pin. + */ +class DB_PUBLIC NetPinRef +{ +public: + /** + * @brief Default constructor + */ + NetPinRef (); + + /** + * @brief Creates a pin reference to the given pin of the current circuit + */ + NetPinRef (size_t pin_id); + + /** + * @brief Copy constructor + */ + NetPinRef (const NetPinRef &other); + + /** + * @brief Assignment + */ + NetPinRef &operator= (const NetPinRef &other); + + /** + * @brief Comparison + */ + bool operator< (const NetPinRef &other) const + { + return m_pin_id < other.m_pin_id; + } + + /** + * @brief Equality + */ + bool operator== (const NetPinRef &other) const + { + return (m_pin_id == other.m_pin_id); + } + + /** + * @brief Gets the pin reference (const version) + */ + size_t pin_id () const + { + return m_pin_id; + } + + /** + * @brief Gets the pin reference from the pin id + * If the pin cannot be resolved, null is returned. + */ + const Pin *pin () const; + + /** + * @brief Gets the net the pin lives in + */ + Net *net () + { + return mp_net; + } + + /** + * @brief Gets the net the pin lives in (const version) + */ + const Net *net () const + { + return mp_net; + } + +private: + friend class Net; + + size_t m_pin_id; + Net *mp_net; + + /** + * @brief Sets the net the terminal lives in + */ + void set_net (Net *net) + { + mp_net = net; + } +}; + +/** + * @brief A reference to a pin inside a net + * + * This object describes a connection to a pin of a subcircuit. + */ +class DB_PUBLIC NetSubcircuitPinRef +{ +public: + /** + * @brief Default constructor + */ + NetSubcircuitPinRef (); + + /** + * @brief Creates a pin reference to the given pin of the given subcircuit + */ + NetSubcircuitPinRef (SubCircuit *circuit, size_t pin_id); + + /** + * @brief Copy constructor + */ + NetSubcircuitPinRef (const NetSubcircuitPinRef &other); + + /** + * @brief Assignment + */ + NetSubcircuitPinRef &operator= (const NetSubcircuitPinRef &other); + + /** + * @brief Comparison + */ + bool operator< (const NetSubcircuitPinRef &other) const + { + if (mp_subcircuit != other.mp_subcircuit) { + return mp_subcircuit < other.mp_subcircuit; + } + return m_pin_id < other.m_pin_id; + } + + /** + * @brief Equality + */ + bool operator== (const NetSubcircuitPinRef &other) const + { + return (mp_subcircuit == other.mp_subcircuit && m_pin_id == other.m_pin_id); + } + + /** + * @brief Gets the pin reference (const version) + */ + size_t pin_id () const + { + return m_pin_id; + } + + /** + * @brief Gets the pin reference from the pin id + * If the pin cannot be resolved, null is returned. + */ + const Pin *pin () const; + + /** + * @brief Gets the subcircuit reference + */ + SubCircuit *subcircuit () + { + return mp_subcircuit; + } + + /** + * @brief Gets the subcircuit reference (const version) + */ + const SubCircuit *subcircuit () const + { + return mp_subcircuit; + } + + /** + * @brief Gets the net the pin lives in + */ + Net *net () + { + return mp_net; + } + + /** + * @brief Gets the net the pin lives in (const version) + */ + const Net *net () const + { + return mp_net; + } + +private: + friend class Net; + + size_t m_pin_id; + SubCircuit *mp_subcircuit; + Net *mp_net; + + /** + * @brief Sets the net the terminal lives in + */ + void set_net (Net *net) + { + mp_net = net; + } +}; + +/** + * @brief A net + * + * A net connects terminals of devices and pins or circuits + */ +class DB_PUBLIC Net + : public tl::Object +{ +public: + typedef std::list terminal_list; + typedef terminal_list::const_iterator const_terminal_iterator; + typedef terminal_list::iterator terminal_iterator; + typedef std::list pin_list; + typedef pin_list::const_iterator const_pin_iterator; + typedef pin_list::iterator pin_iterator; + typedef std::list subcircuit_pin_list; + typedef subcircuit_pin_list::const_iterator const_subcircuit_pin_iterator; + typedef subcircuit_pin_list::iterator subcircuit_pin_iterator; + + /** + * @brief Constructor + * Creates an empty circuit. + */ + Net (); + + /** + * @brief Creates a empty net with the give name + */ + Net (const std::string &name); + + /** + * @brief Copy constructor + */ + Net (const Net &other); + + /** + * @brief Destructor + */ + ~Net (); + + /** + * @brief Assignment + */ + Net &operator= (const Net &other); + + /** + * @brief Gets the circuit the net lives in + * This pointer is 0 if the net is not part of a circuit. + */ + Circuit *circuit () + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the net lives in (const version) + * This pointer is 0 if the net is not part of a circuit. + */ + const Circuit *circuit () const + { + return mp_circuit; + } + + /** + * @brief Clears the circuit + */ + void clear (); + + /** + * @brief Sets the name of the circuit + */ + void set_name (const std::string &name); + + /** + * @brief Gets the name of the circuit + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Gets the expanded name + * + * The "expanded name" is a non-empty name for the net. It uses the + * cluster ID if no name is set. + */ + std::string expanded_name () const; + + /** + * @brief Gets the qualified name + * + * The qualified name is like the expanded name, but preceeded with the + * Circuit name if known (e.g. "CIRCUIT:NET") + */ + std::string qname () const; + + /** + * @brief Sets the cluster ID of this net + * + * The cluster ID links the net to a cluster from the + * hierarchical layout netlist extractor. + */ + void set_cluster_id (size_t ci); + + /** + * @brief Gets the cluster ID + */ + size_t cluster_id () const + { + return m_cluster_id; + } + + /** + * @brief Adds a pin to this net + */ + void add_pin (const NetPinRef &pin); + + /** + * @brief Erases the given pin from this net + */ + void erase_pin (pin_iterator iter); + + /** + * @brief Begin iterator for the pins of the net (const version) + */ + const_pin_iterator begin_pins () const + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (const version) + */ + const_pin_iterator end_pins () const + { + return m_pins.end (); + } + + /** + * @brief Begin iterator for the pins of the net (non-const version) + */ + pin_iterator begin_pins () + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (non-const version) + */ + pin_iterator end_pins () + { + return m_pins.end (); + } + + /** + * @brief Adds a subcircuit pin to this net + */ + void add_subcircuit_pin (const NetSubcircuitPinRef &pin); + + /** + * @brief Erases the given subcircuit pin from this net + */ + void erase_subcircuit_pin (subcircuit_pin_iterator iter); + + /** + * @brief Begin iterator for the pins of the net (const version) + */ + const_subcircuit_pin_iterator begin_subcircuit_pins () const + { + return m_subcircuit_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (const version) + */ + const_subcircuit_pin_iterator end_subcircuit_pins () const + { + return m_subcircuit_pins.end (); + } + + /** + * @brief Begin iterator for the pins of the net (non-const version) + */ + subcircuit_pin_iterator begin_subcircuit_pins () + { + return m_subcircuit_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (non-const version) + */ + subcircuit_pin_iterator end_subcircuit_pins () + { + return m_subcircuit_pins.end (); + } + + /** + * @brief Adds a terminal to this net + */ + void add_terminal (const NetTerminalRef &terminal); + + /** + * @brief Erases the given terminal from this net + */ + void erase_terminal (terminal_iterator iter); + + /** + * @brief Begin iterator for the terminals of the net (const version) + */ + const_terminal_iterator begin_terminals () const + { + return m_terminals.begin (); + } + + /** + * @brief End iterator for the terminals of the net (const version) + */ + const_terminal_iterator end_terminals () const + { + return m_terminals.end (); + } + + /** + * @brief Begin iterator for the terminals of the net (non-const version) + */ + terminal_iterator begin_terminals () + { + return m_terminals.begin (); + } + + /** + * @brief End iterator for the terminals of the net (non-const version) + */ + terminal_iterator end_terminals () + { + return m_terminals.end (); + } + + /** + * @brief Returns true, if the net is floating (has no or only a single connection) + */ + bool is_floating () const + { + return (m_pins.size () + m_subcircuit_pins.size () + m_terminals.size ()) < 2; + } + + /** + * @brief Returns true, if the net is an internal node (connects two terminals only) + */ + bool is_internal () const + { + return m_pins.size () == 0 && m_subcircuit_pins.size () == 0 && m_terminals.size () == 2; + } + + /** + * @brief Returns the number of outgoing pins connected + */ + size_t pin_count () const + { + return m_pins.size (); + } + + /** + * @brief Returns the number of subcircuit pins connected + */ + size_t subcircuit_pin_count () const + { + return m_subcircuit_pins.size (); + } + + /** + * @brief Returns the number of terminals connected + */ + size_t terminal_count () const + { + return m_terminals.size (); + } + +private: + friend class Circuit; + + terminal_list m_terminals; + pin_list m_pins; + subcircuit_pin_list m_subcircuit_pins; + std::string m_name; + size_t m_cluster_id; + Circuit *mp_circuit; + + void set_circuit (Circuit *circuit); +}; + +} + +#endif diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 8ca118e60..21dced73c 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -27,1202 +27,6 @@ namespace db { -// -------------------------------------------------------------------------------- -// Pin class implementation - -Pin::Pin () - : m_id (0) -{ - // .. nothing yet .. -} - -Pin::Pin (const std::string &name) - : m_name (name), m_id (0) -{ - // .. nothing yet .. -} - -// -------------------------------------------------------------------------------- -// Device class implementation - -Device::Device () - : mp_device_class (0), m_id (0), mp_circuit (0) -{ - // .. nothing yet .. -} - -Device::~Device () -{ - for (std::vector::const_iterator t = m_terminal_refs.begin (); t != m_terminal_refs.end (); ++t) { - if (*t != Net::terminal_iterator () && (*t)->net ()) { - (*t)->net ()->erase_terminal (*t); - } - } -} - -Device::Device (DeviceClass *device_class, const std::string &name) - : mp_device_class (device_class), m_name (name), m_id (0), mp_circuit (0) -{ - // .. nothing yet .. -} - -Device::Device (const Device &other) - : mp_device_class (0), m_id (0), mp_circuit (0) -{ - operator= (other); -} - -Device &Device::operator= (const Device &other) -{ - if (this != &other) { - m_name = other.m_name; - mp_device_class = other.mp_device_class; - } - return *this; -} - -void Device::set_circuit (Circuit *circuit) -{ - mp_circuit = circuit; -} - -void Device::set_name (const std::string &n) -{ - m_name = n; - if (mp_circuit) { - mp_circuit->m_device_by_name.invalidate (); - } -} - -void Device::set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter) -{ - if (m_terminal_refs.size () < terminal_id + 1) { - m_terminal_refs.resize (terminal_id + 1, Net::terminal_iterator ()); - } - m_terminal_refs [terminal_id] = iter; -} - -const Net *Device::net_for_terminal (size_t terminal_id) const -{ - if (terminal_id < m_terminal_refs.size ()) { - Net::terminal_iterator p = m_terminal_refs [terminal_id]; - if (p != Net::terminal_iterator ()) { - return p->net (); - } - } - return 0; -} - -void Device::connect_terminal (size_t terminal_id, Net *net) -{ - if (net_for_terminal (terminal_id) == net) { - return; - } - - if (terminal_id < m_terminal_refs.size ()) { - Net::terminal_iterator p = m_terminal_refs [terminal_id]; - if (p != Net::terminal_iterator () && p->net ()) { - p->net ()->erase_terminal (p); - } - m_terminal_refs [terminal_id] = Net::terminal_iterator (); - } - - if (net) { - net->add_terminal (NetTerminalRef (this, terminal_id)); - } -} - -double Device::parameter_value (size_t param_id) const -{ - if (m_parameters.size () > param_id) { - return m_parameters [param_id]; - } else if (mp_device_class) { - const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (param_id); - if (pd) { - return pd->default_value (); - } - } - return 0.0; -} - -void Device::set_parameter_value (size_t param_id, double v) -{ - if (m_parameters.size () <= param_id) { - - // resize the parameter vector with default values - size_t from_size = m_parameters.size (); - m_parameters.resize (param_id + 1, 0.0); - - if (mp_device_class) { - for (size_t n = from_size; n < param_id; ++n) { - const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (n); - if (pd) { - m_parameters [n] = pd->default_value (); - } - } - } - - } - - m_parameters [param_id] = v; -} - -double Device::parameter_value (const std::string &name) const -{ - return device_class () ? parameter_value (device_class ()->parameter_id_for_name (name)) : 0.0; -} - -void Device::set_parameter_value (const std::string &name, double v) -{ - if (device_class ()) { - set_parameter_value (device_class ()->parameter_id_for_name (name), v); - } -} - - -// -------------------------------------------------------------------------------- -// SubCircuit class implementation - -SubCircuit::SubCircuit () - : m_id (0), mp_circuit (0) -{ - // .. nothing yet .. -} - -SubCircuit::~SubCircuit() -{ - for (std::vector::const_iterator p = m_pin_refs.begin (); p != m_pin_refs.end (); ++p) { - if (*p != Net::subcircuit_pin_iterator () && (*p)->net ()) { - (*p)->net ()->erase_subcircuit_pin (*p); - } - } -} - -SubCircuit::SubCircuit (Circuit *circuit, const std::string &name) - : m_circuit_ref (0), m_name (name), m_id (0), mp_circuit (0) -{ - set_circuit_ref (circuit); -} - -SubCircuit::SubCircuit (const SubCircuit &other) - : m_id (0), mp_circuit (0) -{ - operator= (other); -} - -SubCircuit &SubCircuit::operator= (const SubCircuit &other) -{ - if (this != &other) { - m_name = other.m_name; - m_trans = other.m_trans; - set_circuit_ref (const_cast (other.circuit_ref ())); - } - return *this; -} - -void SubCircuit::set_name (const std::string &n) -{ - m_name = n; - if (mp_circuit) { - mp_circuit->m_subcircuit_by_name.invalidate (); - } -} - -void SubCircuit::set_trans (const db::DCplxTrans &t) -{ - m_trans = t; -} - -void SubCircuit::set_pin_ref_for_pin (size_t pin_id, Net::subcircuit_pin_iterator iter) -{ - if (m_pin_refs.size () < pin_id + 1) { - m_pin_refs.resize (pin_id + 1, Net::subcircuit_pin_iterator ()); - } - m_pin_refs [pin_id] = iter; -} - -void SubCircuit::set_circuit_ref (Circuit *c) -{ - if (m_circuit_ref.get ()) { - m_circuit_ref->unregister_ref (this); - } - m_circuit_ref.reset (c); - if (m_circuit_ref.get ()) { - m_circuit_ref->register_ref (this); - } -} - -const Net *SubCircuit::net_for_pin (size_t pin_id) const -{ - if (pin_id < m_pin_refs.size ()) { - Net::subcircuit_pin_iterator p = m_pin_refs [pin_id]; - if (p != Net::subcircuit_pin_iterator ()) { - return p->net (); - } - } - return 0; -} - -void SubCircuit::connect_pin (size_t pin_id, Net *net) -{ - if (net_for_pin (pin_id) == net) { - return; - } - - if (pin_id < m_pin_refs.size ()) { - Net::subcircuit_pin_iterator p = m_pin_refs [pin_id]; - if (p != Net::subcircuit_pin_iterator () && p->net ()) { - p->net ()->erase_subcircuit_pin (p); - } - m_pin_refs [pin_id] = Net::subcircuit_pin_iterator (); - } - - if (net) { - net->add_subcircuit_pin (NetSubcircuitPinRef (this, pin_id)); - } -} - -// -------------------------------------------------------------------------------- -// NetTerminalRef class implementation - -NetTerminalRef::NetTerminalRef () - : m_terminal_id (0), mp_device (0), mp_net (0) -{ - // .. nothing yet .. -} - -NetTerminalRef::NetTerminalRef (Device *device, size_t terminal_id) - : m_terminal_id (terminal_id), mp_device (device), mp_net (0) -{ - // .. nothing yet .. -} - -NetTerminalRef::NetTerminalRef (const NetTerminalRef &other) - : m_terminal_id (other.m_terminal_id), mp_device (other.mp_device), mp_net (0) -{ - // .. nothing yet .. -} - -NetTerminalRef &NetTerminalRef::operator= (const NetTerminalRef &other) -{ - if (this != &other) { - m_terminal_id = other.m_terminal_id; - mp_device = other.mp_device; - } - return *this; -} - -const DeviceTerminalDefinition * -NetTerminalRef::terminal_def () const -{ - const DeviceClass *dc = device_class (); - if (dc) { - return dc->terminal_definition (m_terminal_id); - } else { - return 0; - } -} - -const DeviceClass * -NetTerminalRef::device_class () const -{ - return mp_device ? mp_device->device_class () : 0; -} - -// -------------------------------------------------------------------------------- -// NetPinRef class implementation - -NetPinRef::NetPinRef () - : m_pin_id (0), mp_net (0) -{ - // .. nothing yet .. -} - -NetPinRef::NetPinRef (size_t pin_id) - : m_pin_id (pin_id), mp_net (0) -{ - // .. nothing yet .. -} - -NetPinRef::NetPinRef (const NetPinRef &other) - : m_pin_id (other.m_pin_id), mp_net (0) -{ - // .. nothing yet .. -} - -NetPinRef &NetPinRef::operator= (const NetPinRef &other) -{ - if (this != &other) { - m_pin_id = other.m_pin_id; - } - return *this; -} - -const Pin *NetPinRef::pin () const -{ - if (mp_net && mp_net->circuit ()) { - return mp_net->circuit ()->pin_by_id (m_pin_id); - } - return 0; -} - -// -------------------------------------------------------------------------------- -// NetSubcircuitPinRef class implementation - -NetSubcircuitPinRef::NetSubcircuitPinRef () - : m_pin_id (0), mp_subcircuit (0), mp_net (0) -{ - // .. nothing yet .. -} - -NetSubcircuitPinRef::NetSubcircuitPinRef (SubCircuit *circuit, size_t pin_id) - : m_pin_id (pin_id), mp_subcircuit (circuit), mp_net (0) -{ - // .. nothing yet .. -} - -NetSubcircuitPinRef::NetSubcircuitPinRef (const NetSubcircuitPinRef &other) - : m_pin_id (other.m_pin_id), mp_subcircuit (other.mp_subcircuit), mp_net (0) -{ - // .. nothing yet .. -} - -NetSubcircuitPinRef &NetSubcircuitPinRef::operator= (const NetSubcircuitPinRef &other) -{ - if (this != &other) { - m_pin_id = other.m_pin_id; - mp_subcircuit = other.mp_subcircuit; - } - return *this; -} - -const Pin *NetSubcircuitPinRef::pin () const -{ - if (mp_subcircuit && mp_subcircuit->circuit_ref ()) { - return mp_subcircuit->circuit_ref ()->pin_by_id (m_pin_id); - } - return 0; -} - -// -------------------------------------------------------------------------------- -// Net class implementation - -Net::Net () - : m_cluster_id (0), mp_circuit (0) -{ - // .. nothing yet .. -} - -Net::Net (const std::string &name) - : m_cluster_id (0), mp_circuit (0) -{ - m_name = name; -} - -Net::Net (const Net &other) - : m_cluster_id (0), mp_circuit (0) -{ - operator= (other); -} - -Net &Net::operator= (const Net &other) -{ - if (this != &other) { - - clear (); - - m_name = other.m_name; - m_cluster_id = other.m_cluster_id; - - for (const_subcircuit_pin_iterator i = other.begin_subcircuit_pins (); i != other.end_subcircuit_pins (); ++i) { - add_subcircuit_pin (*i); - } - - for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { - add_pin (*i); - } - - for (const_terminal_iterator i = other.begin_terminals (); i != other.end_terminals (); ++i) { - add_terminal (*i); - } - - } - return *this; -} - -Net::~Net () -{ - clear (); -} - -void Net::clear () -{ - m_name.clear (); - m_cluster_id = 0; - - while (! m_terminals.empty ()) { - erase_terminal (begin_terminals ()); - } - - while (! m_pins.empty ()) { - erase_pin (begin_pins ()); - } - - while (! m_subcircuit_pins.empty ()) { - erase_subcircuit_pin (begin_subcircuit_pins ()); - } -} - -void Net::set_name (const std::string &name) -{ - m_name = name; - if (mp_circuit) { - mp_circuit->m_net_by_name.invalidate (); - } -} - -std::string Net::qname () const -{ - if (circuit ()) { - return circuit ()->name () + ":" + expanded_name (); - } else { - return expanded_name (); - } -} - -std::string Net::expanded_name () const -{ - if (name ().empty ()) { - if (cluster_id () > std::numeric_limits::max () / 2) { - // avoid printing huge ID numbers for internal cluster IDs - return "$I" + tl::to_string ((std::numeric_limits::max () - cluster_id ()) + 1); - } else { - return "$" + tl::to_string (cluster_id ()); - } - } else { - return name (); - } -} - -void Net::set_cluster_id (size_t ci) -{ - m_cluster_id = ci; - if (mp_circuit) { - mp_circuit->m_net_by_cluster_id.invalidate (); - } -} - -void Net::add_pin (const NetPinRef &pin) -{ - m_pins.push_back (pin); - NetPinRef &new_pin = m_pins.back (); - new_pin.set_net (this); - - if (mp_circuit) { - mp_circuit->set_pin_ref_for_pin (new_pin.pin_id (), --m_pins.end ()); - } -} - -void Net::add_subcircuit_pin (const NetSubcircuitPinRef &pin) -{ - m_subcircuit_pins.push_back (pin); - NetSubcircuitPinRef &new_pin = m_subcircuit_pins.back (); - new_pin.set_net (this); - - tl_assert (pin.subcircuit () != 0); - new_pin.subcircuit ()->set_pin_ref_for_pin (new_pin.pin_id (), --m_subcircuit_pins.end ()); -} - -void Net::erase_pin (pin_iterator iter) -{ - if (mp_circuit) { - mp_circuit->set_pin_ref_for_pin (iter->pin_id (), pin_iterator ()); - } - m_pins.erase (iter); -} - -void Net::erase_subcircuit_pin (subcircuit_pin_iterator iter) -{ - if (iter->subcircuit ()) { - iter->subcircuit ()->set_pin_ref_for_pin (iter->pin_id (), subcircuit_pin_iterator ()); - } - m_subcircuit_pins.erase (iter); -} - -void Net::add_terminal (const NetTerminalRef &terminal) -{ - if (! terminal.device ()) { - return; - } - - m_terminals.push_back (terminal); - NetTerminalRef &new_terminal = m_terminals.back (); - new_terminal.set_net (this); - new_terminal.device ()->set_terminal_ref_for_terminal (new_terminal.terminal_id (), --m_terminals.end ()); -} - -void Net::erase_terminal (terminal_iterator iter) -{ - if (iter->device ()) { - iter->device ()->set_terminal_ref_for_terminal (iter->terminal_id (), terminal_iterator ()); - } - m_terminals.erase (iter); -} - -void Net::set_circuit (Circuit *circuit) -{ - mp_circuit = circuit; -} - -// -------------------------------------------------------------------------------- -// Circuit class implementation - -Circuit::Circuit () - : mp_netlist (0), - m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), - m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), - m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), - m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices), - m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), - m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), - m_index (0) -{ - m_devices.changed ().add (this, &Circuit::devices_changed); - m_nets.changed ().add (this, &Circuit::nets_changed); - m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed); -} - -Circuit::Circuit (const Circuit &other) - : mp_netlist (0), - m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), - m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), - m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), - m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices), - m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), - m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), - m_index (0) -{ - operator= (other); - m_devices.changed ().add (this, &Circuit::devices_changed); - m_nets.changed ().add (this, &Circuit::nets_changed); - m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed); -} - -Circuit::~Circuit () -{ - m_devices.changed ().remove (this, &Circuit::devices_changed); - m_nets.changed ().remove (this, &Circuit::nets_changed); - m_subcircuits.changed ().remove (this, &Circuit::subcircuits_changed); - - // the default destructor will make the nets access "this" to unregister the - // objects - hence we have to do this explicitly. - m_nets.clear (); - m_subcircuits.clear (); - m_devices.clear (); -} - -Circuit &Circuit::operator= (const Circuit &other) -{ - if (this != &other) { - - clear (); - - m_name = other.m_name; - m_pins = other.m_pins; - - std::map device_table; - for (const_device_iterator i = other.begin_devices (); i != other.end_devices (); ++i) { - Device *d = new Device (*i); - device_table [i.operator-> ()] = d; - add_device (d); - } - - std::map sc_table; - for (const_subcircuit_iterator i = other.begin_subcircuits (); i != other.end_subcircuits (); ++i) { - SubCircuit *sc = new SubCircuit (*i); - sc_table [i.operator-> ()] = sc; - add_subcircuit (sc); - } - - for (const_net_iterator i = other.begin_nets (); i != other.end_nets (); ++i) { - - // translate the net - Net *n = new Net (); - n->set_cluster_id (i->cluster_id ()); - n->set_name (i->name ()); - add_net (n); - - for (Net::const_terminal_iterator p = i->begin_terminals (); p != i->end_terminals (); ++p) { - std::map::const_iterator m = device_table.find (p->device ()); - tl_assert (m != device_table.end ()); - n->add_terminal (NetTerminalRef (m->second, p->terminal_id ())); - } - - for (Net::const_pin_iterator p = i->begin_pins (); p != i->end_pins (); ++p) { - n->add_pin (NetPinRef (p->pin_id ())); - } - - for (Net::const_subcircuit_pin_iterator p = i->begin_subcircuit_pins (); p != i->end_subcircuit_pins (); ++p) { - std::map::const_iterator m = sc_table.find (p->subcircuit ()); - tl_assert (m != sc_table.end ()); - n->add_subcircuit_pin (NetSubcircuitPinRef (m->second, p->pin_id ())); - } - - } - - } - - return *this; -} - -void Circuit::set_netlist (Netlist *netlist) -{ - mp_netlist = netlist; -} - -const Pin *Circuit::pin_by_id (size_t id) const -{ - if (id >= m_pins.size ()) { - return 0; - } else { - return &m_pins [id]; - } -} - -const Pin *Circuit::pin_by_name (const std::string &name) const -{ - for (Circuit::const_pin_iterator p = begin_pins (); p != end_pins (); ++p) { - if (p->name () == name) { - return p.operator-> (); - } - } - return 0; -} - -void Circuit::devices_changed () -{ - m_device_by_id.invalidate (); - m_device_by_name.invalidate (); -} - -void Circuit::subcircuits_changed () -{ - m_subcircuit_by_id.invalidate (); - m_subcircuit_by_name.invalidate (); - - if (mp_netlist) { - mp_netlist->invalidate_topology (); - } -} - -void Circuit::nets_changed () -{ - m_net_by_cluster_id.invalidate (); - m_net_by_name.invalidate (); -} - -void Circuit::clear () -{ - m_name.clear (); - m_pins.clear (); - m_devices.clear (); - m_nets.clear (); - m_subcircuits.clear (); -} - -void Circuit::set_name (const std::string &name) -{ - m_name = name; - if (mp_netlist) { - mp_netlist->m_circuit_by_name.invalidate (); - } -} - -void Circuit::set_cell_index (const db::cell_index_type ci) -{ - m_cell_index = ci; - if (mp_netlist) { - mp_netlist->m_circuit_by_cell_index.invalidate (); - } -} - -Circuit::child_circuit_iterator Circuit::begin_children () -{ - tl_assert (mp_netlist != 0); - return mp_netlist->child_circuits (this).begin (); -} - -Circuit::child_circuit_iterator Circuit::end_children () -{ - tl_assert (mp_netlist != 0); - return mp_netlist->child_circuits (this).end (); -} - -Circuit::const_child_circuit_iterator Circuit::begin_children () const -{ - tl_assert (mp_netlist != 0); - return reinterpret_cast &> (mp_netlist->child_circuits (const_cast (this))).begin (); -} - -Circuit::const_child_circuit_iterator Circuit::end_children () const -{ - tl_assert (mp_netlist != 0); - return reinterpret_cast &> (mp_netlist->child_circuits (const_cast (this))).end (); -} - -Circuit::child_circuit_iterator Circuit::begin_parents () -{ - tl_assert (mp_netlist != 0); - return mp_netlist->parent_circuits (this).begin (); -} - -Circuit::child_circuit_iterator Circuit::end_parents () -{ - tl_assert (mp_netlist != 0); - return mp_netlist->parent_circuits (this).end (); -} - -Circuit::const_child_circuit_iterator Circuit::begin_parents () const -{ - tl_assert (mp_netlist != 0); - return reinterpret_cast &> (mp_netlist->parent_circuits (const_cast (this))).begin (); -} - -Circuit::const_child_circuit_iterator Circuit::end_parents () const -{ - tl_assert (mp_netlist != 0); - return reinterpret_cast &> (mp_netlist->parent_circuits (const_cast (this))).end (); -} - -const Pin &Circuit::add_pin (const std::string &name) -{ - m_pins.push_back (Pin (name)); - m_pins.back ().set_id (m_pins.size () - 1); - return m_pins.back (); -} - -void Circuit::add_net (Net *net) -{ - m_nets.push_back (net); - net->set_circuit (this); -} - -void Circuit::remove_net (Net *net) -{ - m_nets.erase (net); -} - -void Circuit::add_device (Device *device) -{ - device->set_circuit (this); - - size_t id = 0; - if (! m_devices.empty ()) { - tl_assert (m_devices.back () != 0); - id = m_devices.back ()->id (); - } - device->set_id (id + 1); - - m_devices.push_back (device); -} - -void Circuit::remove_device (Device *device) -{ - m_devices.erase (device); -} - -void Circuit::add_subcircuit (SubCircuit *subcircuit) -{ - subcircuit->set_circuit (this); - - size_t id = 0; - if (! m_subcircuits.empty ()) { - tl_assert (m_subcircuits.back () != 0); - id = m_subcircuits.back ()->id (); - } - subcircuit->set_id (id + 1); - - m_subcircuits.push_back (subcircuit); -} - -void Circuit::remove_subcircuit (SubCircuit *subcircuit) -{ - m_subcircuits.erase (subcircuit); -} - -void Circuit::register_ref (SubCircuit *r) -{ - m_refs.push_back (r); -} - -void Circuit::unregister_ref (SubCircuit *r) -{ - m_refs.erase (r); -} - -void Circuit::translate_circuits (const std::map &map) -{ - for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) { - std::map::const_iterator m = map.find (i->circuit_ref ()); - tl_assert (m != map.end ()); - i->set_circuit_ref (m->second); - } -} - -void Circuit::translate_device_classes (const std::map &map) -{ - for (device_iterator i = m_devices.begin (); i != m_devices.end (); ++i) { - std::map::const_iterator m = map.find (i->device_class ()); - tl_assert (m != map.end ()); - i->set_device_class (m->second); - } -} - -void Circuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter) -{ - if (m_pin_refs.size () < pin_id + 1) { - m_pin_refs.resize (pin_id + 1, Net::pin_iterator ()); - } - m_pin_refs [pin_id] = iter; -} - -const Net *Circuit::net_for_pin (size_t pin_id) const -{ - if (pin_id < m_pin_refs.size ()) { - Net::pin_iterator p = m_pin_refs [pin_id]; - if (p != Net::pin_iterator ()) { - return p->net (); - } - } - return 0; -} - -void Circuit::connect_pin (size_t pin_id, Net *net) -{ - if (net_for_pin (pin_id) == net) { - return; - } - - if (pin_id < m_pin_refs.size ()) { - Net::pin_iterator p = m_pin_refs [pin_id]; - if (p != Net::pin_iterator () && p->net ()) { - p->net ()->erase_pin (p); - } - m_pin_refs [pin_id] = Net::pin_iterator (); - } - - if (net) { - net->add_pin (NetPinRef (pin_id)); - } -} - -void Circuit::purge_nets () -{ - std::vector nets_to_be_purged; - for (net_iterator n = begin_nets (); n != end_nets (); ++n) { - if (n->is_floating ()) { - nets_to_be_purged.push_back (n.operator-> ()); - } - } - for (std::vector::const_iterator n = nets_to_be_purged.begin (); n != nets_to_be_purged.end (); ++n) { - delete *n; - } -} - -/** - * @brief Sanity check for device to be removed - */ -static void check_device_before_remove (db::Circuit *c, const db::Device *d) -{ - if (d->device_class () == 0) { - throw tl::Exception (tl::to_string (tr ("Internal error: No device class after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name ()); - } - const std::vector &pd = d->device_class ()->terminal_definitions (); - for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { - if (d->net_for_terminal (p->id ()) != 0) { - throw tl::Exception (tl::to_string (tr ("Internal error: Terminal still connected after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name () + ", terminal=" + p->name ()); - } - } -} - -bool Circuit::combine_parallel_devices (const db::DeviceClass &cls) -{ - typedef std::vector key_type; - std::map > combination_candidates; - - bool any = false; - - // identify the candidates for combination - all devices sharing the same nets - // are candidates for combination in parallel mode - for (device_iterator d = begin_devices (); d != end_devices (); ++d) { - - if (tl::id_of (d->device_class ()) != tl::id_of (&cls)) { - continue; - } - - key_type k; - const std::vector &terminals = cls.terminal_definitions (); - for (std::vector::const_iterator p = terminals.begin (); p != terminals.end (); ++p) { - const db::Net *n = d->net_for_terminal (p->id ()); - if (n) { - k.push_back (n); - } - } - - std::sort (k.begin (), k.end ()); - k.erase (std::unique (k.begin (), k.end ()), k.end ()); - combination_candidates[k].push_back (d.operator-> ()); - - } - - // actually combine the devices - for (std::map >::iterator cc = combination_candidates.begin (); cc != combination_candidates.end (); ++cc) { - - std::vector &cl = cc->second; - for (size_t i = 0; i < cl.size () - 1; ++i) { - for (size_t j = i + 1; j < cl.size (); ) { - if (cls.combine_devices (cl [i], cl [j])) { - check_device_before_remove (this, cl [j]); // sanity check - delete cl [j]; - cl.erase (cl.begin () + j); - any = true; - } else { - ++j; - } - } - } - - } - - return any; -} - -static std::pair attached_two_devices (db::Net &net, const db::DeviceClass &cls) -{ - if (net.begin_pins () != net.end_pins ()) { - return std::make_pair ((db::Device *) 0, (db::Device *) 0); - } - - db::Device *d1 = 0, *d2 = 0; - - Net::terminal_iterator p = net.begin_terminals (); - if (p == net.end_terminals () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { - return std::make_pair ((db::Device *) 0, (db::Device *) 0); - } else { - d1 = p->device (); - } - - ++p; - if (p == net.end_terminals () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { - return std::make_pair ((db::Device *) 0, (db::Device *) 0); - } else { - d2 = p->device (); - } - - ++p; - if (p != net.end_terminals () || d1 == d2 || !d1 || !d2) { - return std::make_pair ((db::Device *) 0, (db::Device *) 0); - } else { - return std::make_pair (d1, d2); - } -} - -template -static bool same_or_swapped (const std::pair &p1, const std::pair &p2) -{ - return (p1.first == p2.first && p1.second == p2.second) || (p1.first == p2.second && p1.second == p2.first); -} - -bool Circuit::combine_serial_devices(const db::DeviceClass &cls) -{ - bool any = false; - - for (net_iterator n = begin_nets (); n != end_nets (); ++n) { - - std::pair dd = attached_two_devices (*n, cls); - if (! dd.first) { - continue; - } - - // The net is an internal node: the devices attached to this internal node are - // combination candidates if the number of nets emerging from the attached device pair (not counting - // the internal node we just found) does not exceed the number of pins available for the - // new device. - - std::vector other_nets; - - const std::vector &terminals = cls.terminal_definitions (); - for (std::vector::const_iterator p = terminals.begin (); p != terminals.end (); ++p) { - db::Net *on; - on = dd.first->net_for_terminal (p->id ()); - if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) { - other_nets.push_back (on); - } - on = dd.second->net_for_terminal (p->id ()); - if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) { - other_nets.push_back (on); - } - } - - std::sort (other_nets.begin (), other_nets.end ()); - other_nets.erase (std::unique (other_nets.begin (), other_nets.end ()), other_nets.end ()); - - if (other_nets.size () <= cls.terminal_definitions().size ()) { - - // found a combination candidate - if (cls.combine_devices (dd.first, dd.second)) { - check_device_before_remove (this, dd.second); // sanity check - delete dd.second; - any = true; - } - - } - - } - - return any; -} - -void Circuit::combine_devices () -{ - tl_assert (netlist () != 0); - - for (Netlist::device_class_iterator dc = netlist ()->begin_device_classes (); dc != netlist ()->end_device_classes (); ++dc) { - - // repeat the combination step unless no combination happens - this is required to take care of combinations that arise after - // other combinations have been realized. - bool any = true; - while (any) { - - any = false; - - if (dc->supports_parallel_combination ()) { - if (combine_parallel_devices (*dc)) { - any = true; - } - } - if (dc->supports_serial_combination ()) { - if (combine_serial_devices (*dc)) { - any = true; - } - } - - } - - } -} - -// -------------------------------------------------------------------------------- -// DeviceClass class implementation - -DeviceClass::DeviceClass () - : mp_netlist (0) -{ - // .. nothing yet .. -} - -DeviceClass::DeviceClass (const DeviceClass &other) - : mp_netlist (0) -{ - operator= (other); -} - -DeviceClass &DeviceClass::operator= (const DeviceClass &other) -{ - if (this != &other) { - m_terminal_definitions = other.m_terminal_definitions; - m_name = other.m_name; - m_description = other.m_description; - } - return *this; -} - -const DeviceTerminalDefinition &DeviceClass::add_terminal_definition (const DeviceTerminalDefinition &pd) -{ - m_terminal_definitions.push_back (pd); - m_terminal_definitions.back ().set_id (m_terminal_definitions.size () - 1); - return m_terminal_definitions.back (); -} - -void DeviceClass::clear_terminal_definitions () -{ - m_terminal_definitions.clear (); -} - -const DeviceTerminalDefinition *DeviceClass::terminal_definition (size_t id) const -{ - if (id < m_terminal_definitions.size ()) { - return & m_terminal_definitions [id]; - } else { - return 0; - } -} - -const DeviceParameterDefinition &DeviceClass::add_parameter_definition (const DeviceParameterDefinition &pd) -{ - m_parameter_definitions.push_back (pd); - m_parameter_definitions.back ().set_id (m_parameter_definitions.size () - 1); - return m_parameter_definitions.back (); -} - -void DeviceClass::clear_parameter_definitions () -{ - m_parameter_definitions.clear (); -} - -const DeviceParameterDefinition *DeviceClass::parameter_definition (size_t id) const -{ - if (id < m_parameter_definitions.size ()) { - return & m_parameter_definitions [id]; - } else { - return 0; - } -} - -bool DeviceClass::has_parameter_with_name (const std::string &name) const -{ - const std::vector &pd = parameter_definitions (); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (i->name () == name) { - return true; - } - } - return false; -} - -size_t DeviceClass::parameter_id_for_name (const std::string &name) const -{ - const std::vector &pd = parameter_definitions (); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (i->name () == name) { - return i->id (); - } - } - throw tl::Exception (tl::to_string (tr ("Invalid parameter name")) + ": '" + name + "'"); -} - -bool DeviceClass::has_terminal_with_name (const std::string &name) const -{ - const std::vector &td = terminal_definitions (); - for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { - if (i->name () == name) { - return true; - } - } - return false; -} - -size_t DeviceClass::terminal_id_for_name (const std::string &name) const -{ - const std::vector &td = terminal_definitions (); - for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { - if (i->name () == name) { - return i->id (); - } - } - throw tl::Exception (tl::to_string (tr ("Invalid terminal name")) + ": '" + name + "'"); -} - // -------------------------------------------------------------------------------- // Netlist class implementation diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index c944f325a..36dc2676c 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -24,2092 +24,16 @@ #define _HDR_dbNetlist #include "dbCommon.h" -#include "dbTypes.h" -#include "dbTrans.h" -#include "tlObjectCollection.h" +#include "dbCircuit.h" +#include "dbDeviceClass.h" + #include "tlVector.h" -#include "tlUniqueId.h" -#include "gsiObject.h" -#include -#include #include - namespace db { -class Circuit; -class SubCircuit; -class Pin; -class Device; -class DeviceClass; -class DeviceTerminalDefinition; -class Netlist; -class Net; - -/** - * @brief A getter for the ID of an object - */ -template -struct id_attribute -{ - typedef size_t attr_type; - size_t operator() (const T *t) const { return t->id (); } - bool has (const T * /*t*/) const { return true; } -}; - -/** - * @brief A getter for the cluster ID of an object - */ -template -struct cluster_id_attribute -{ - typedef size_t attr_type; - attr_type operator() (const T *t) const { return t->cluster_id (); } - bool has (const T * /*t*/) const { return true; } -}; - -/** - * @brief A getter for the cluster ID of an object - */ -template -struct cell_index_attribute -{ - typedef db::cell_index_type attr_type; - attr_type operator() (const T *t) const { return t->cell_index (); } - bool has (const T * /*t*/) const { return true; } -}; - -/** - * @brief A getter for the name of an object - */ -template -struct name_attribute -{ - typedef std::string attr_type; - const attr_type &operator() (const T *t) const { return t->name (); } - bool has (const T *t) const { return ! t->name ().empty (); } -}; - -/** - * @brief An id-to-object translation table - */ -template -class object_by_attr -{ -public: - typedef typename ATTR::attr_type attr_type; - typedef typename I::value_type value_type; - - object_by_attr (T *self, I (T::*bi) (), I (T::*ei) ()) : mp_self (self), m_bi (bi), m_ei (ei), m_valid (false) - { - // .. nothing yet .. - } - - void invalidate () - { - m_valid = false; - m_map.clear (); - } - - value_type *object_by (const attr_type &attr) const - { - if (! m_valid) { - validate (); - } - typename std::map::const_iterator m = m_map.find (attr); - return m == m_map.end () ? 0 : m->second; - } - -private: - T *mp_self; - I (T::*m_bi) (); - I (T::*m_ei) (); - mutable bool m_valid; - mutable std::map m_map; - - void validate () const - { - ATTR attr; - m_map.clear (); - for (I i = (mp_self->*m_bi) (); i != (mp_self->*m_ei) (); ++i) { - if (attr.has (i.operator-> ())) { - m_map.insert (std::make_pair (attr (i.operator-> ()), i.operator-> ())); - } - } - m_valid = true; - } -}; - -/** - * @brief A reference to a terminal of a device - * - * A terminal must always refer to a device inside the current circuit. - */ -class DB_PUBLIC NetTerminalRef -{ -public: - /** - * @brief Default constructor - */ - NetTerminalRef (); - - /** - * @brief Creates a pin reference to the given pin of the current circuit - */ - NetTerminalRef (Device *device, size_t terminal_id); - - /** - * @brief Copy constructor - */ - NetTerminalRef (const NetTerminalRef &other); - - /** - * @brief Assignment - */ - NetTerminalRef &operator= (const NetTerminalRef &other); - - /** - * @brief Comparison - */ - bool operator< (const NetTerminalRef &other) const - { - if (mp_device != other.mp_device) { - return mp_device < other.mp_device; - } - return m_terminal_id < other.m_terminal_id; - } - - /** - * @brief Equality - */ - bool operator== (const NetTerminalRef &other) const - { - return (mp_device == other.mp_device && m_terminal_id == other.m_terminal_id); - } - - /** - * @brief Gets the device reference - */ - Device *device () - { - return mp_device; - } - - /** - * @brief Gets the device reference (const version) - */ - const Device *device () const - { - return mp_device; - } - - /** - * @brief Gets the terminal index - */ - size_t terminal_id () const - { - return m_terminal_id; - } - - /** - * @brief Gets the terminal definition - * - * Returns 0 if the terminal is not a valid terminal reference. - */ - const DeviceTerminalDefinition *terminal_def () const; - - /** - * @brief Returns the device class - */ - const DeviceClass *device_class () const; - - /** - * @brief Gets the net the terminal lives in - */ - Net *net () - { - return mp_net; - } - - /** - * @brief Gets the net the terminal lives in (const version) - */ - const Net *net () const - { - return mp_net; - } - -private: - friend class Net; - - size_t m_terminal_id; - Device *mp_device; - Net *mp_net; - - /** - * @brief Sets the net the terminal lives in - */ - void set_net (Net *net) - { - mp_net = net; - } -}; - -/** - * @brief A reference to a pin inside a net - * - * This object describes a connection to an outgoing pin. - */ -class DB_PUBLIC NetPinRef -{ -public: - /** - * @brief Default constructor - */ - NetPinRef (); - - /** - * @brief Creates a pin reference to the given pin of the current circuit - */ - NetPinRef (size_t pin_id); - - /** - * @brief Copy constructor - */ - NetPinRef (const NetPinRef &other); - - /** - * @brief Assignment - */ - NetPinRef &operator= (const NetPinRef &other); - - /** - * @brief Comparison - */ - bool operator< (const NetPinRef &other) const - { - return m_pin_id < other.m_pin_id; - } - - /** - * @brief Equality - */ - bool operator== (const NetPinRef &other) const - { - return (m_pin_id == other.m_pin_id); - } - - /** - * @brief Gets the pin reference (const version) - */ - size_t pin_id () const - { - return m_pin_id; - } - - /** - * @brief Gets the pin reference from the pin id - * If the pin cannot be resolved, null is returned. - */ - const Pin *pin () const; - - /** - * @brief Gets the net the pin lives in - */ - Net *net () - { - return mp_net; - } - - /** - * @brief Gets the net the pin lives in (const version) - */ - const Net *net () const - { - return mp_net; - } - -private: - friend class Net; - - size_t m_pin_id; - Net *mp_net; - - /** - * @brief Sets the net the terminal lives in - */ - void set_net (Net *net) - { - mp_net = net; - } -}; - -/** - * @brief A reference to a pin inside a net - * - * This object describes a connection to a pin of a subcircuit. - */ -class DB_PUBLIC NetSubcircuitPinRef -{ -public: - /** - * @brief Default constructor - */ - NetSubcircuitPinRef (); - - /** - * @brief Creates a pin reference to the given pin of the given subcircuit - */ - NetSubcircuitPinRef (SubCircuit *circuit, size_t pin_id); - - /** - * @brief Copy constructor - */ - NetSubcircuitPinRef (const NetSubcircuitPinRef &other); - - /** - * @brief Assignment - */ - NetSubcircuitPinRef &operator= (const NetSubcircuitPinRef &other); - - /** - * @brief Comparison - */ - bool operator< (const NetSubcircuitPinRef &other) const - { - if (mp_subcircuit != other.mp_subcircuit) { - return mp_subcircuit < other.mp_subcircuit; - } - return m_pin_id < other.m_pin_id; - } - - /** - * @brief Equality - */ - bool operator== (const NetSubcircuitPinRef &other) const - { - return (mp_subcircuit == other.mp_subcircuit && m_pin_id == other.m_pin_id); - } - - /** - * @brief Gets the pin reference (const version) - */ - size_t pin_id () const - { - return m_pin_id; - } - - /** - * @brief Gets the pin reference from the pin id - * If the pin cannot be resolved, null is returned. - */ - const Pin *pin () const; - - /** - * @brief Gets the subcircuit reference - */ - SubCircuit *subcircuit () - { - return mp_subcircuit; - } - - /** - * @brief Gets the subcircuit reference (const version) - */ - const SubCircuit *subcircuit () const - { - return mp_subcircuit; - } - - /** - * @brief Gets the net the pin lives in - */ - Net *net () - { - return mp_net; - } - - /** - * @brief Gets the net the pin lives in (const version) - */ - const Net *net () const - { - return mp_net; - } - -private: - friend class Net; - - size_t m_pin_id; - SubCircuit *mp_subcircuit; - Net *mp_net; - - /** - * @brief Sets the net the terminal lives in - */ - void set_net (Net *net) - { - mp_net = net; - } -}; - -/** - * @brief A net - * - * A net connects terminals of devices and pins or circuits - */ -class DB_PUBLIC Net - : public tl::Object -{ -public: - typedef std::list terminal_list; - typedef terminal_list::const_iterator const_terminal_iterator; - typedef terminal_list::iterator terminal_iterator; - typedef std::list pin_list; - typedef pin_list::const_iterator const_pin_iterator; - typedef pin_list::iterator pin_iterator; - typedef std::list subcircuit_pin_list; - typedef subcircuit_pin_list::const_iterator const_subcircuit_pin_iterator; - typedef subcircuit_pin_list::iterator subcircuit_pin_iterator; - - /** - * @brief Constructor - * Creates an empty circuit. - */ - Net (); - - /** - * @brief Creates a empty net with the give name - */ - Net (const std::string &name); - - /** - * @brief Copy constructor - */ - Net (const Net &other); - - /** - * @brief Destructor - */ - ~Net (); - - /** - * @brief Assignment - */ - Net &operator= (const Net &other); - - /** - * @brief Gets the circuit the net lives in - * This pointer is 0 if the net is not part of a circuit. - */ - Circuit *circuit () - { - return mp_circuit; - } - - /** - * @brief Gets the circuit the net lives in (const version) - * This pointer is 0 if the net is not part of a circuit. - */ - const Circuit *circuit () const - { - return mp_circuit; - } - - /** - * @brief Clears the circuit - */ - void clear (); - - /** - * @brief Sets the name of the circuit - */ - void set_name (const std::string &name); - - /** - * @brief Gets the name of the circuit - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief Gets the expanded name - * - * The "expanded name" is a non-empty name for the net. It uses the - * cluster ID if no name is set. - */ - std::string expanded_name () const; - - /** - * @brief Gets the qualified name - * - * The qualified name is like the expanded name, but preceeded with the - * Circuit name if known (e.g. "CIRCUIT:NET") - */ - std::string qname () const; - - /** - * @brief Sets the cluster ID of this net - * - * The cluster ID links the net to a cluster from the - * hierarchical layout netlist extractor. - */ - void set_cluster_id (size_t ci); - - /** - * @brief Gets the cluster ID - */ - size_t cluster_id () const - { - return m_cluster_id; - } - - /** - * @brief Adds a pin to this net - */ - void add_pin (const NetPinRef &pin); - - /** - * @brief Erases the given pin from this net - */ - void erase_pin (pin_iterator iter); - - /** - * @brief Begin iterator for the pins of the net (const version) - */ - const_pin_iterator begin_pins () const - { - return m_pins.begin (); - } - - /** - * @brief End iterator for the pins of the net (const version) - */ - const_pin_iterator end_pins () const - { - return m_pins.end (); - } - - /** - * @brief Begin iterator for the pins of the net (non-const version) - */ - pin_iterator begin_pins () - { - return m_pins.begin (); - } - - /** - * @brief End iterator for the pins of the net (non-const version) - */ - pin_iterator end_pins () - { - return m_pins.end (); - } - - /** - * @brief Adds a subcircuit pin to this net - */ - void add_subcircuit_pin (const NetSubcircuitPinRef &pin); - - /** - * @brief Erases the given subcircuit pin from this net - */ - void erase_subcircuit_pin (subcircuit_pin_iterator iter); - - /** - * @brief Begin iterator for the pins of the net (const version) - */ - const_subcircuit_pin_iterator begin_subcircuit_pins () const - { - return m_subcircuit_pins.begin (); - } - - /** - * @brief End iterator for the pins of the net (const version) - */ - const_subcircuit_pin_iterator end_subcircuit_pins () const - { - return m_subcircuit_pins.end (); - } - - /** - * @brief Begin iterator for the pins of the net (non-const version) - */ - subcircuit_pin_iterator begin_subcircuit_pins () - { - return m_subcircuit_pins.begin (); - } - - /** - * @brief End iterator for the pins of the net (non-const version) - */ - subcircuit_pin_iterator end_subcircuit_pins () - { - return m_subcircuit_pins.end (); - } - - /** - * @brief Adds a terminal to this net - */ - void add_terminal (const NetTerminalRef &terminal); - - /** - * @brief Erases the given terminal from this net - */ - void erase_terminal (terminal_iterator iter); - - /** - * @brief Begin iterator for the terminals of the net (const version) - */ - const_terminal_iterator begin_terminals () const - { - return m_terminals.begin (); - } - - /** - * @brief End iterator for the terminals of the net (const version) - */ - const_terminal_iterator end_terminals () const - { - return m_terminals.end (); - } - - /** - * @brief Begin iterator for the terminals of the net (non-const version) - */ - terminal_iterator begin_terminals () - { - return m_terminals.begin (); - } - - /** - * @brief End iterator for the terminals of the net (non-const version) - */ - terminal_iterator end_terminals () - { - return m_terminals.end (); - } - - /** - * @brief Returns true, if the net is floating (has no or only a single connection) - */ - bool is_floating () const - { - return (m_pins.size () + m_subcircuit_pins.size () + m_terminals.size ()) < 2; - } - - /** - * @brief Returns true, if the net is an internal node (connects two terminals only) - */ - bool is_internal () const - { - return m_pins.size () == 0 && m_subcircuit_pins.size () == 0 && m_terminals.size () == 2; - } - - /** - * @brief Returns the number of outgoing pins connected - */ - size_t pin_count () const - { - return m_pins.size (); - } - - /** - * @brief Returns the number of subcircuit pins connected - */ - size_t subcircuit_pin_count () const - { - return m_subcircuit_pins.size (); - } - - /** - * @brief Returns the number of terminals connected - */ - size_t terminal_count () const - { - return m_terminals.size (); - } - -private: - friend class Circuit; - - terminal_list m_terminals; - pin_list m_pins; - subcircuit_pin_list m_subcircuit_pins; - std::string m_name; - size_t m_cluster_id; - Circuit *mp_circuit; - - void set_circuit (Circuit *circuit); -}; - -/** - * @brief The definition of a pin of a circuit - * - * A pin is some place other nets can connect to a circuit. - */ -class DB_PUBLIC Pin -{ -public: - /** - * @brief Default constructor - */ - Pin (); - - /** - * @brief Creates a pin with the given name. - */ - Pin (const std::string &name); - - /** - * @brief Gets the name of the pin - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief Gets the ID of the pin (only pins inside circuits have valid ID's) - */ - size_t id () const - { - return m_id; - } - -private: - friend class Circuit; - - std::string m_name; - size_t m_id; - - void set_id (size_t id) - { - m_id = id; - } -}; - -/** - * @brief An actual device - * - * This class represents the incarnation of a specific device. - * The device has a class which specifies a type. This class - * is intended for subclassing. - * A specific device subclass is supposed to correspond to - * a specific device class. - */ -class DB_PUBLIC Device - : public tl::Object -{ -public: - typedef std::vector > global_connections; - typedef global_connections::const_iterator global_connections_iterator; - - /** - * @brief Default constructor - */ - Device (); - - /** - * @brief The constructor - */ - Device (DeviceClass *device_class, const std::string &name = std::string ()); - - /** - * @brief Copy constructor - */ - Device (const Device &other); - - /** - * @brief Assignment - */ - Device &operator= (const Device &other); - - /** - * @brief Destructor - */ - ~Device (); - - /** - * @brief Gets the device class - */ - const DeviceClass *device_class () const - { - return mp_device_class; - } - - /** - * @brief Gets the device ID - * The ID is a unique integer which identifies the device. - * It can be used to retrieve the device from the circuit using Circuit::device_by_id. - * When assigned, the device ID is not 0. - */ - size_t id () const - { - return m_id; - } - - /** - * @brief Gets the circuit the device lives in (const version) - * This pointer is 0 if the device isn't added to a circuit - */ - const Circuit *circuit () const - { - return mp_circuit; - } - - /** - * @brief Gets the circuit the device lives in (non-const version) - * This pointer is 0 if the device isn't added to a circuit - */ - Circuit *circuit () - { - return mp_circuit; - } - - /** - * @brief Sets the name - */ - void set_name (const std::string &n); - - /** - * @brief Gets the name - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief Gets the net attached to a specific terminal - * Returns 0 if no net is attached. - */ - const Net *net_for_terminal (size_t terminal_id) const; - - /** - * @brief Gets the net attached to a specific terminal (non-const version) - * Returns 0 if no net is attached. - */ - Net *net_for_terminal (size_t terminal_id) - { - return const_cast (((const Device *) this)->net_for_terminal (terminal_id)); - } - - /** - * @brief Connects the given terminal to the given net - * If the net is 0 the terminal is disconnected. - * If non-null, a NetTerminalRef object will be inserted into the - * net and connected with the given terminal. - * If the terminal is connected to a global net, it will be - * disconnected from there. - */ - void connect_terminal (size_t terminal_id, Net *net); - - /** - * @brief Gets the value for the parameter with the given ID - */ - double parameter_value (size_t param_id) const; - - /** - * @brief Sets the value for the parameter with the given ID - */ - void set_parameter_value (size_t param_id, double v); - - /** - * @brief Gets the value for the parameter with the given name - * If the name is not valid, an exception is thrown. - */ - double parameter_value (const std::string &name) const; - - /** - * @brief Sets the value for the parameter with the given name - * If the name is not valid, an exception is thrown. - */ - void set_parameter_value (const std::string &name, double v); - -private: - friend class Circuit; - friend class Net; - - DeviceClass *mp_device_class; - std::string m_name; - std::vector m_terminal_refs; - std::vector m_parameters; - size_t m_id; - Circuit *mp_circuit; - - /** - * @brief Sets the terminal reference for a specific terminal - */ - void set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter); - - /** - * @brief Sets the device class - */ - void set_device_class (DeviceClass *dc) - { - mp_device_class = dc; - } - - /** - * @brief Sets the device ID - */ - void set_id (size_t id) - { - m_id = id; - } - - /** - * @brief Sets the circuit - */ - void set_circuit (Circuit *circuit); -}; - -/** - * @brief A subcircuit of a circuit - * - * This class essentially is a reference to another circuit - */ -class DB_PUBLIC SubCircuit - : public tl::Object -{ -public: - typedef tl::vector connected_net_list; - - /** - * @brief Default constructor - */ - SubCircuit (); - - /** - * @brief Copy constructor - */ - SubCircuit (const SubCircuit &other); - - /** - * @brief Assignment - */ - SubCircuit &operator= (const SubCircuit &other); - - /** - * @brief Creates a subcircuit reference to the given circuit - */ - SubCircuit (Circuit *circuit_ref, const std::string &name = std::string ()); - - /** - * @brief Destructor - */ - ~SubCircuit (); - - /** - * @brief Gets the subcircuit ID - * The ID is a unique integer which identifies the subcircuit. - * It can be used to retrieve the subcircuit from the circuit using Circuit::subcircuit_by_id. - * When assigned, the subcircuit ID is not 0. - */ - size_t id () const - { - return m_id; - } - - /** - * @brief Gets the circuit the subcircuit lives in (const version) - * This pointer is 0 if the subcircuit isn't added to a circuit - */ - const Circuit *circuit () const - { - return mp_circuit; - } - - /** - * @brief Gets the circuit the subcircuit lives in (non-const version) - * This pointer is 0 if the subcircuit isn't added to a circuit - */ - Circuit *circuit () - { - return mp_circuit; - } - - /** - * @brief Gets the circuit the reference points to (const version) - */ - const Circuit *circuit_ref () const - { - return m_circuit_ref.get (); - } - - /** - * @brief Gets the circuit the reference points to (non-const version) - */ - Circuit *circuit_ref () - { - return m_circuit_ref.get (); - } - - /** - * @brief Sets the name of the subcircuit - * - * The name is one way to identify the subcircuit. The transformation is - * another one. - */ - void set_name (const std::string &n); - - /** - * @brief Gets the name of the subcircuit - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief Sets the transformation describing the subcircuit - * - * The transformation is a natural description of a subcircuit - * (in contrast to the name) when deriving it from a layout. - */ - void set_trans (const db::DCplxTrans &trans); - - /** - * @brief Gets the transformation describing the subcircuit - */ - const db::DCplxTrans &trans () const - { - return m_trans; - } - - /** - * @brief Gets the net attached to a specific pin - * Returns 0 if no net is attached. - */ - const Net *net_for_pin (size_t pin_id) const; - - /** - * @brief Gets the net attached to a specific pin (non-const version) - * Returns 0 if no net is attached. - */ - Net *net_for_pin (size_t pin_id) - { - return const_cast (((const SubCircuit *) this)->net_for_pin (pin_id)); - } - - /** - * @brief Connects the given pin to the given net - * If the net is 0 the pin is disconnected. - * If non-null, a NetPinRef object will be inserted into the - * net and connected with the given pin. - */ - void connect_pin (size_t pin_id, Net *net); - -private: - friend class Circuit; - friend class Net; - - tl::weak_ptr m_circuit_ref; - std::string m_name; - db::DCplxTrans m_trans; - std::vector m_pin_refs; - size_t m_id; - Circuit *mp_circuit; - - /** - * @brief Sets the pin reference for a specific pin - */ - void set_pin_ref_for_pin (size_t ppin_id, Net::subcircuit_pin_iterator iter); - - /** - * @brief Sets the circuit reference - */ - void set_circuit_ref (Circuit *c); - - /** - * @brief Sets the circuit the subcircuit belongs to - */ - void set_circuit (Circuit *c) - { - mp_circuit = c; - } - - /** - * @brief Sets the device ID - */ - void set_id (size_t id) - { - m_id = id; - } -}; - -/** - * @brief A circuit - * - * A circuit is a list of nets, of subcircuit references and actual - * devices. - */ -class DB_PUBLIC Circuit - : public gsi::ObjectBase, public tl::Object -{ -public: - typedef tl::vector pin_list; - typedef pin_list::const_iterator const_pin_iterator; - typedef pin_list::iterator pin_iterator; - typedef tl::shared_collection device_list; - typedef device_list::const_iterator const_device_iterator; - typedef device_list::iterator device_iterator; - typedef tl::shared_collection net_list; - typedef net_list::const_iterator const_net_iterator; - typedef net_list::iterator net_iterator; - typedef tl::shared_collection subcircuit_list; - typedef subcircuit_list::const_iterator const_subcircuit_iterator; - typedef subcircuit_list::iterator subcircuit_iterator; - typedef tl::weak_collection::const_iterator const_refs_iterator; - typedef tl::weak_collection::iterator refs_iterator; - typedef tl::vector::const_iterator child_circuit_iterator; - typedef tl::vector::const_iterator const_child_circuit_iterator; - typedef tl::vector::const_iterator parent_circuit_iterator; - typedef tl::vector::const_iterator const_parent_circuit_iterator; - - /** - * @brief Constructor - * - * Creates an empty circuit. - */ - Circuit (); - - /** - * @brief Copy constructor - */ - Circuit (const Circuit &other); - - /** - * @brief Destructor - */ - ~Circuit (); - - /** - * @brief Assignment - */ - Circuit &operator= (const Circuit &other); - - /** - * @brief Gets the netlist the circuit lives in - * This pointer is 0 if the circuit is not part of a netlist. - */ - Netlist *netlist () - { - return mp_netlist; - } - - /** - * @brief Gets the netlist the circuit lives in (const version) - * This pointer is 0 if the circuit is not part of a netlist. - */ - const Netlist *netlist () const - { - return mp_netlist; - } - - /** - * @brief Clears the circuit - */ - void clear (); - - /** - * @brief Sets the name of the circuit - */ - void set_name (const std::string &name); - - /** - * @brief Gets the name of the circuit - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief The index of the circuit in the netlist - * CAUTION: this attribute is used for internal purposes and may not be valid always. - */ - size_t index () const - { - return m_index; - } - - /** - * @brief Sets the layout cell reference for this circuit - * - * The layout cell reference links a circuit to a layout cell. - */ - void set_cell_index (const db::cell_index_type ci); - - /** - * @brief Gets the layout cell index - */ - db::cell_index_type cell_index () const - { - return m_cell_index; - } - - /** - * @brief Gets the references to this circuit (begin, non-const version) - * This iterator will deliver all subcircuits referencing this circuit - */ - refs_iterator begin_refs () - { - return m_refs.begin (); - } - - /** - * @brief Gets the references to this circuit (end, non-const version) - * This iterator will deliver all subcircuits referencing this circuit - */ - refs_iterator end_refs () - { - return m_refs.end (); - } - - /** - * @brief Gets the references to this circuit (begin, const version) - * This iterator will deliver all subcircuits referencing this circuit - */ - const_refs_iterator begin_refs () const - { - return m_refs.begin (); - } - - /** - * @brief Gets the child circuits iterator (begin) - * The child circuits are the circuits referenced by all subcircuits - * in the circuit. - */ - child_circuit_iterator begin_children (); - - /** - * @brief Gets the child circuits iterator (end) - */ - child_circuit_iterator end_children (); - - /** - * @brief Gets the child circuits iterator (begin, const version) - * The child circuits are the circuits referenced by all subcircuits - * in the circuit. - */ - const_child_circuit_iterator begin_children () const; - - /** - * @brief Gets the child circuits iterator (end, const version) - */ - const_child_circuit_iterator end_children () const; - - /** - * @brief Gets the parent circuits iterator (begin) - * The parents circuits are the circuits referencing this circuit via subcircuits. - */ - parent_circuit_iterator begin_parents (); - - /** - * @brief Gets the parent circuits iterator (end) - */ - parent_circuit_iterator end_parents (); - - /** - * @brief Gets the parent circuits iterator (begin, const version) - * The parents circuits are the circuits referencing this circuit via subcircuits. - */ - const_parent_circuit_iterator begin_parents () const; - - /** - * @brief Gets the parent circuits iterator (end, const version) - */ - const_parent_circuit_iterator end_parents () const; - - /** - * @brief Gets the references to this circuit (end, const version) - * This iterator will deliver all subcircuits referencing this circuit - */ - const_refs_iterator end_refs () const - { - return m_refs.end (); - } - - /** - * @brief Adds a pin to this circuit - * The circuit takes over ownership of the object. - */ - const Pin &add_pin(const std::string &name); - - /** - * @brief Begin iterator for the pins of the circuit (non-const version) - */ - pin_iterator begin_pins () - { - return m_pins.begin (); - } - - /** - * @brief End iterator for the pins of the circuit (non-const version) - */ - pin_iterator end_pins () - { - return m_pins.end (); - } - - /** - * @brief Gets the pin count - */ - size_t pin_count () const - { - return m_pins.size (); - } - - /** - * @brief Gets the pin by ID (the ID is basically the index) - */ - const Pin *pin_by_id (size_t id) const; - - /** - * @brief Gets the pin by name - * - * If there is no pin with that name, null is returned. - * NOTE: this is a linear search, so it's performance may not be good for many pins. - */ - const Pin *pin_by_name (const std::string &name) const; - - /** - * @brief Begin iterator for the pins of the circuit (const version) - */ - const_pin_iterator begin_pins () const - { - return m_pins.begin (); - } - - /** - * @brief End iterator for the pins of the circuit (const version) - */ - const_pin_iterator end_pins () const - { - return m_pins.end (); - } - - /** - * @brief Adds a net to this circuit - * - * The circuit takes over ownership of the object. - */ - void add_net (Net *net); - - /** - * @brief Deletes a net from the circuit - */ - void remove_net (Net *net); - - /** - * @brief Begin iterator for the nets of the circuit (non-const version) - */ - net_iterator begin_nets () - { - return m_nets.begin (); - } - - /** - * @brief End iterator for the nets of the circuit (non-const version) - */ - net_iterator end_nets () - { - return m_nets.end (); - } - - /** - * @brief Begin iterator for the nets of the circuit (const version) - */ - const_net_iterator begin_nets () const - { - return m_nets.begin (); - } - - /** - * @brief End iterator for the nets of the circuit (const version) - */ - const_net_iterator end_nets () const - { - return m_nets.end (); - } - - /** - * @brief Gets the net from a given cluster ID (const version) - * - * If the cluster ID is not valid, null is returned. - */ - const Net *net_by_cluster_id (size_t cluster_id) const - { - return (const_cast (this)->net_by_cluster_id (cluster_id)); - } - - /** - * @brief Gets the net from a given cluster ID (non-const version) - * - * If the cluster ID is not valid, null is returned. - */ - Net *net_by_cluster_id (size_t cluster_id) - { - return m_net_by_cluster_id.object_by (cluster_id); - } - - /** - * @brief Gets the net from a given name (const version) - * - * If the name is not valid, null is returned. - */ - const Net *net_by_name (const std::string &name) const - { - return (const_cast (this)->net_by_name (name)); - } - - /** - * @brief Gets the net from a given name (non-const version) - * - * If the name is not valid, null is returned. - */ - Net *net_by_name (const std::string &name) - { - return m_net_by_name.object_by (name); - } - - /** - * @brief Adds a device to this circuit - * - * The circuit takes over ownership of the object. - */ - void add_device (Device *device); - - /** - * @brief Deletes a device from the circuit - */ - void remove_device (Device *device); - - /** - * @brief Gets the device from a given ID (const version) - * - * If the ID is not valid, null is returned. - */ - const Device *device_by_id (size_t id) const - { - return (const_cast (this)->device_by_id (id)); - } - - /** - * @brief Gets the device from a given ID (non-const version) - * - * If the ID is not valid, null is returned. - */ - Device *device_by_id (size_t id) - { - return m_device_by_id.object_by (id); - } - - /** - * @brief Gets the device from a given name (const version) - * - * If the name is not valid, null is returned. - */ - const Device *device_by_name (const std::string &name) const - { - return (const_cast (this)->device_by_name (name)); - } - - /** - * @brief Gets the device from a given name (non-const version) - * - * If the name is not valid, null is returned. - */ - Device *device_by_name (const std::string &name) - { - return m_device_by_name.object_by (name); - } - - /** - * @brief Begin iterator for the devices of the circuit (non-const version) - */ - device_iterator begin_devices () - { - return m_devices.begin (); - } - - /** - * @brief End iterator for the devices of the circuit (non-const version) - */ - device_iterator end_devices () - { - return m_devices.end (); - } - - /** - * @brief Begin iterator for the devices of the circuit (const version) - */ - const_device_iterator begin_devices () const - { - return m_devices.begin (); - } - - /** - * @brief End iterator for the devices of the circuit (const version) - */ - const_device_iterator end_devices () const - { - return m_devices.end (); - } - - /** - * @brief Adds a subcircuit to this circuit - * - * The circuit takes over ownership of the object. - */ - void add_subcircuit (SubCircuit *subcircuit); - - /** - * @brief Deletes a subcircuit from the circuit - */ - void remove_subcircuit (SubCircuit *subcircuit); - - /** - * @brief Gets the subcircuit from a given ID (const version) - * - * If the ID is not valid, null is returned. - */ - const SubCircuit *subcircuit_by_id (size_t id) const - { - return (const_cast (this)->subcircuit_by_id (id)); - } - - /** - * @brief Gets the subcircuit from a given ID (non-const version) - * - * If the ID is not valid, null is returned. - */ - SubCircuit *subcircuit_by_id (size_t id) - { - return m_subcircuit_by_id.object_by (id); - } - - /** - * @brief Gets the subcircuit from a given name (const version) - * - * If the name is not valid, null is returned. - */ - const SubCircuit *subcircuit_by_name (const std::string &name) const - { - return (const_cast (this)->subcircuit_by_name (name)); - } - - /** - * @brief Gets the subcircuit from a given name (non-const version) - * - * If the name is not valid, null is returned. - */ - SubCircuit *subcircuit_by_name (const std::string &name) - { - return m_subcircuit_by_name.object_by (name); - } - - /** - * @brief Begin iterator for the subcircuits of the circuit (non-const version) - */ - subcircuit_iterator begin_subcircuits () - { - return m_subcircuits.begin (); - } - - /** - * @brief End iterator for the subcircuits of the circuit (non-const version) - */ - subcircuit_iterator end_subcircuits () - { - return m_subcircuits.end (); - } - - /** - * @brief Begin iterator for the subcircuits of the circuit (const version) - */ - const_subcircuit_iterator begin_subcircuits () const - { - return m_subcircuits.begin (); - } - - /** - * @brief End iterator for the subcircuits of the circuit (const version) - */ - const_subcircuit_iterator end_subcircuits () const - { - return m_subcircuits.end (); - } - - /** - * @brief Gets the connected net for a pin with the given id - * - * Returns 0 if the pin is not connected to a net. - */ - const Net *net_for_pin (size_t pin_id) const; - - /** - * @brief Gets the connected net for a pin with the given id (non-const version) - * - * Returns 0 if the pin is not connected to a net. - */ - Net *net_for_pin (size_t pin_id) - { - return const_cast (((const Circuit *) this)->net_for_pin (pin_id)); - } - - /** - * @brief Connects the given pin to the given net - * If the net is 0 the pin is disconnected. - * If non-null, a NetPinRef object will be inserted into the - * net and connected with the given pin. - */ - void connect_pin (size_t pin_id, Net *net); - - /** - * @brief Purge unused nets - * - * This method will purge all nets which return "floating". - */ - void purge_nets (); - - /** - * @brief Combine devices - * - * This method will combine devices that can be combined according - * to their device classes "combine_devices" method. - */ - void combine_devices (); - -private: - friend class Netlist; - friend class Net; - friend class SubCircuit; - friend class Device; - - std::string m_name; - db::cell_index_type m_cell_index; - net_list m_nets; - pin_list m_pins; - device_list m_devices; - subcircuit_list m_subcircuits; - Netlist *mp_netlist; - std::vector m_pin_refs; - object_by_attr > m_device_by_id; - object_by_attr > m_subcircuit_by_id; - object_by_attr > m_net_by_cluster_id; - object_by_attr > m_device_by_name; - object_by_attr > m_subcircuit_by_name; - object_by_attr > m_net_by_name; - tl::weak_collection m_refs; - size_t m_index; - - void set_index (size_t index) - { - m_index = index; - } - - void set_pin_ref_for_pin (size_t ppin_id, Net::pin_iterator iter); - - void register_ref (SubCircuit *sc); - void unregister_ref (SubCircuit *sc); - - void translate_circuits (const std::map &map); - void translate_device_classes (const std::map &map); - void set_netlist (Netlist *netlist); - bool combine_parallel_devices (const db::DeviceClass &cls); - bool combine_serial_devices (const db::DeviceClass &cls); - - void devices_changed (); - void subcircuits_changed (); - void nets_changed (); -}; - -/** - * @brief A device terminal definition - */ -class DB_PUBLIC DeviceTerminalDefinition -{ -public: - /** - * @brief Creates an empty device terminal definition - */ - DeviceTerminalDefinition () - : m_name (), m_description (), m_id (0) - { - // .. nothing yet .. - } - - /** - * @brief Creates a device terminal definition with the given name and description - */ - DeviceTerminalDefinition (const std::string &name, const std::string &description) - : m_name (name), m_description (description), m_id (0) - { - // .. nothing yet .. - } - - /** - * @brief Gets the terminal name - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief Sets the terminal name - */ - void set_name (const std::string &n) - { - m_name = n; - } - - /** - * @brief Gets the terminal description - */ - const std::string &description () const - { - return m_description; - } - - /** - * @brief Sets the terminal description - */ - void set_description (const std::string &d) - { - m_description = d; - } - - /** - * @brief Gets the terminal ID - */ - size_t id () const - { - return m_id; - } - -private: - friend class DeviceClass; - - std::string m_name, m_description; - size_t m_id; - - void set_id (size_t id) - { - m_id = id; - } -}; - -/** - * @brief A device parameter definition - */ -class DB_PUBLIC DeviceParameterDefinition -{ -public: - /** - * @brief Creates an empty device parameter definition - */ - DeviceParameterDefinition () - : m_name (), m_description (), m_default_value (0.0), m_id (0) - { - // .. nothing yet .. - } - - /** - * @brief Creates a device parameter definition with the given name and description - */ - DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0) - : m_name (name), m_description (description), m_default_value (default_value), m_id (0) - { - // .. nothing yet .. - } - - /** - * @brief Gets the parameter name - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief Sets the parameter name - */ - void set_name (const std::string &n) - { - m_name = n; - } - - /** - * @brief Gets the parameter description - */ - const std::string &description () const - { - return m_description; - } - - /** - * @brief Sets the parameter description - */ - void set_description (const std::string &d) - { - m_description = d; - } - - /** - * @brief Gets the parameter default value - */ - double default_value () const - { - return m_default_value; - } - - /** - * @brief Sets the parameter description - */ - void set_default_value (double d) - { - m_default_value = d; - } - - /** - * @brief Gets the parameter ID - */ - size_t id () const - { - return m_id; - } - -private: - friend class DeviceClass; - - std::string m_name, m_description; - double m_default_value; - size_t m_id; - - void set_id (size_t id) - { - m_id = id; - } -}; - -/** - * @brief A device class - * - * A device class describes a type of device. - */ -class DB_PUBLIC DeviceClass - : public gsi::ObjectBase, public tl::Object, public tl::UniqueId -{ -public: - typedef size_t terminal_id_type; - - /** - * @brief Constructor - * - * Creates an empty circuit. - */ - DeviceClass (); - - /** - * @brief Copy constructor - * NOTE: do not use this copy constructor as the device class - * is intended to subclassing. - */ - DeviceClass (const DeviceClass &other); - - /** - * @brief Assignment - * NOTE: do not use this copy constructor as the device class - * is intended to subclassing. - */ - DeviceClass &operator= (const DeviceClass &other); - - /** - * @brief Gets the netlist the device class lives in - */ - db::Netlist *netlist () - { - return mp_netlist; - } - - /** - * @brief Gets the netlist the device class lives in (const version) - */ - const db::Netlist *netlist () const - { - return mp_netlist; - } - - /** - * @brief Gets the name of the device class - * - * The name is a formal name which identifies the class. - */ - const std::string &name () const - { - return m_name; - } - - /** - * @brief Sets the device name - */ - void set_name (const std::string &n) - { - m_name = n; - } - - /** - * @brief Gets the description text for the device class - * - * The description text is a human-readable text that - * identifies the device class. - */ - const std::string &description () const - { - return m_description; - } - - /** - * @brief Sets the description text - */ - void set_description (const std::string &d) - { - m_description = d; - } - - /** - * @brief Gets the terminal definitions - * - * The terminal definitions indicate what terminals the device offers. - * The number of terminals is constant per class. The index of the terminal - * is used as an ID of the terminal, hence the order must be static. - */ - const std::vector &terminal_definitions () const - { - return m_terminal_definitions; - } - - /** - * @brief Adds a terminal definition - */ - const DeviceTerminalDefinition &add_terminal_definition (const DeviceTerminalDefinition &pd); - - /** - * @brief Clears the terminal definition - */ - void clear_terminal_definitions (); - - /** - * @brief Gets the terminal definition from the ID - */ - const DeviceTerminalDefinition *terminal_definition (size_t id) const; - - /** - * @brief Gets the parameter definitions - */ - const std::vector ¶meter_definitions () const - { - return m_parameter_definitions; - } - - /** - * @brief Adds a parameter definition - */ - const DeviceParameterDefinition &add_parameter_definition (const DeviceParameterDefinition &pd); - - /** - * @brief Clears the parameter definition - */ - void clear_parameter_definitions (); - - /** - * @brief Gets the parameter definition from the ID - */ - const DeviceParameterDefinition *parameter_definition (size_t id) const; - - /** - * @brief Returns true, if the device has a parameter with the given name - */ - bool has_parameter_with_name (const std::string &name) const; - - /** - * @brief Returns the parameter ID for the parameter with the given name - * If the name is invalid, an exception is thrown. - */ - size_t parameter_id_for_name (const std::string &name) const; - - /** - * @brief Returns true, if the device has a terminal with the given name - */ - bool has_terminal_with_name (const std::string &name) const; - - /** - * @brief Returns the parameter ID for the terminal with the given name - * If the name is invalid, an exception is thrown. - */ - size_t terminal_id_for_name (const std::string &name) const; - - /** - * @brief Clears the circuit - */ - virtual DeviceClass *clone () const - { - return new DeviceClass (*this); - } - - /** - * @brief Combines two devices - * - * This method shall test, whether the two devices can be combined. Both devices - * are guaranteed to share the same device class (this). - * If they cannot be combined, this method shall do nothing and return false. - * If they can be combined, this method shall reconnect the nets of the first - * device and entirely disconnect the nets of the second device. - * The second device will be deleted afterwards. - */ - virtual bool combine_devices (db::Device * /*a*/, db::Device * /*b*/) const - { - return false; - } - - /** - * @brief Returns true if the device class supports device combination in parallel mode - */ - virtual bool supports_parallel_combination () const - { - return false; - } - - /** - * @brief Returns true if the device class supports device combination in serial mode - */ - virtual bool supports_serial_combination () const - { - return false; - } - -private: - friend class Netlist; - - std::string m_name, m_description; - std::vector m_terminal_definitions; - std::vector m_parameter_definitions; - db::Netlist *mp_netlist; - - void set_netlist (db::Netlist *nl) - { - mp_netlist = nl; - } -}; - /** * @brief The netlist class * diff --git a/src/db/db/dbNetlistUtils.h b/src/db/db/dbNetlistUtils.h new file mode 100644 index 000000000..47911283f --- /dev/null +++ b/src/db/db/dbNetlistUtils.h @@ -0,0 +1,127 @@ + +/* + + 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_dbNetlistUtils +#define _HDR_dbNetlistUtils + +#include "dbCommon.h" + +namespace db +{ + +/** + * @brief A getter for the ID of an object + */ +template +struct id_attribute +{ + typedef size_t attr_type; + size_t operator() (const T *t) const { return t->id (); } + bool has (const T * /*t*/) const { return true; } +}; + +/** + * @brief A getter for the cluster ID of an object + */ +template +struct cluster_id_attribute +{ + typedef size_t attr_type; + attr_type operator() (const T *t) const { return t->cluster_id (); } + bool has (const T * /*t*/) const { return true; } +}; + +/** + * @brief A getter for the cluster ID of an object + */ +template +struct cell_index_attribute +{ + typedef db::cell_index_type attr_type; + attr_type operator() (const T *t) const { return t->cell_index (); } + bool has (const T * /*t*/) const { return true; } +}; + +/** + * @brief A getter for the name of an object + */ +template +struct name_attribute +{ + typedef std::string attr_type; + const attr_type &operator() (const T *t) const { return t->name (); } + bool has (const T *t) const { return ! t->name ().empty (); } +}; + +/** + * @brief An id-to-object translation table + */ +template +class object_by_attr +{ +public: + typedef typename ATTR::attr_type attr_type; + typedef typename I::value_type value_type; + + object_by_attr (T *self, I (T::*bi) (), I (T::*ei) ()) : mp_self (self), m_bi (bi), m_ei (ei), m_valid (false) + { + // .. nothing yet .. + } + + void invalidate () + { + m_valid = false; + m_map.clear (); + } + + value_type *object_by (const attr_type &attr) const + { + if (! m_valid) { + validate (); + } + typename std::map::const_iterator m = m_map.find (attr); + return m == m_map.end () ? 0 : m->second; + } + +private: + T *mp_self; + I (T::*m_bi) (); + I (T::*m_ei) (); + mutable bool m_valid; + mutable std::map m_map; + + void validate () const + { + ATTR attr; + m_map.clear (); + for (I i = (mp_self->*m_bi) (); i != (mp_self->*m_ei) (); ++i) { + if (attr.has (i.operator-> ())) { + m_map.insert (std::make_pair (attr (i.operator-> ()), i.operator-> ())); + } + } + m_valid = true; + } +}; + +} + +#endif diff --git a/src/db/db/dbPin.cc b/src/db/db/dbPin.cc new file mode 100644 index 000000000..74b98b51d --- /dev/null +++ b/src/db/db/dbPin.cc @@ -0,0 +1,43 @@ + +/* + + 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 "dbPin.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// Pin class implementation + +Pin::Pin () + : m_id (0) +{ + // .. nothing yet .. +} + +Pin::Pin (const std::string &name) + : m_name (name), m_id (0) +{ + // .. nothing yet .. +} + +} diff --git a/src/db/db/dbPin.h b/src/db/db/dbPin.h new file mode 100644 index 000000000..925d6e023 --- /dev/null +++ b/src/db/db/dbPin.h @@ -0,0 +1,81 @@ + +/* + + 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_dbPin +#define _HDR_dbPin + +#include "dbCommon.h" + +#include + +namespace db +{ + +/** + * @brief The definition of a pin of a circuit + * + * A pin is some place other nets can connect to a circuit. + */ +class DB_PUBLIC Pin +{ +public: + /** + * @brief Default constructor + */ + Pin (); + + /** + * @brief Creates a pin with the given name. + */ + Pin (const std::string &name); + + /** + * @brief Gets the name of the pin + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Gets the ID of the pin (only pins inside circuits have valid ID's) + */ + size_t id () const + { + return m_id; + } + +private: + friend class Circuit; + + std::string m_name; + size_t m_id; + + void set_id (size_t id) + { + m_id = id; + } +}; + +} + +#endif diff --git a/src/db/db/dbSubCircuit.cc b/src/db/db/dbSubCircuit.cc new file mode 100644 index 000000000..8e6407326 --- /dev/null +++ b/src/db/db/dbSubCircuit.cc @@ -0,0 +1,131 @@ + +/* + + 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 "dbSubCircuit.h" +#include "dbCircuit.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// SubCircuit class implementation + +SubCircuit::SubCircuit () + : m_id (0), mp_circuit (0) +{ + // .. nothing yet .. +} + +SubCircuit::~SubCircuit() +{ + for (std::vector::const_iterator p = m_pin_refs.begin (); p != m_pin_refs.end (); ++p) { + if (*p != Net::subcircuit_pin_iterator () && (*p)->net ()) { + (*p)->net ()->erase_subcircuit_pin (*p); + } + } +} + +SubCircuit::SubCircuit (Circuit *circuit, const std::string &name) + : m_circuit_ref (0), m_name (name), m_id (0), mp_circuit (0) +{ + set_circuit_ref (circuit); +} + +SubCircuit::SubCircuit (const SubCircuit &other) + : m_id (0), mp_circuit (0) +{ + operator= (other); +} + +SubCircuit &SubCircuit::operator= (const SubCircuit &other) +{ + if (this != &other) { + m_name = other.m_name; + m_trans = other.m_trans; + set_circuit_ref (const_cast (other.circuit_ref ())); + } + return *this; +} + +void SubCircuit::set_name (const std::string &n) +{ + m_name = n; + if (mp_circuit) { + mp_circuit->m_subcircuit_by_name.invalidate (); + } +} + +void SubCircuit::set_trans (const db::DCplxTrans &t) +{ + m_trans = t; +} + +void SubCircuit::set_pin_ref_for_pin (size_t pin_id, Net::subcircuit_pin_iterator iter) +{ + if (m_pin_refs.size () < pin_id + 1) { + m_pin_refs.resize (pin_id + 1, Net::subcircuit_pin_iterator ()); + } + m_pin_refs [pin_id] = iter; +} + +void SubCircuit::set_circuit_ref (Circuit *c) +{ + if (m_circuit_ref.get ()) { + m_circuit_ref->unregister_ref (this); + } + m_circuit_ref.reset (c); + if (m_circuit_ref.get ()) { + m_circuit_ref->register_ref (this); + } +} + +const Net *SubCircuit::net_for_pin (size_t pin_id) const +{ + if (pin_id < m_pin_refs.size ()) { + Net::subcircuit_pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::subcircuit_pin_iterator ()) { + return p->net (); + } + } + return 0; +} + +void SubCircuit::connect_pin (size_t pin_id, Net *net) +{ + if (net_for_pin (pin_id) == net) { + return; + } + + if (pin_id < m_pin_refs.size ()) { + Net::subcircuit_pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::subcircuit_pin_iterator () && p->net ()) { + p->net ()->erase_subcircuit_pin (p); + } + m_pin_refs [pin_id] = Net::subcircuit_pin_iterator (); + } + + if (net) { + net->add_subcircuit_pin (NetSubcircuitPinRef (this, pin_id)); + } +} + +} diff --git a/src/db/db/dbSubCircuit.h b/src/db/db/dbSubCircuit.h new file mode 100644 index 000000000..a28b8211a --- /dev/null +++ b/src/db/db/dbSubCircuit.h @@ -0,0 +1,216 @@ + +/* + + 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_dbSubCircuit +#define _HDR_dbSubCircuit + +#include "dbCommon.h" +#include "dbTrans.h" +#include "dbNet.h" + +#include "tlObject.h" + +#include +#include + +namespace db +{ + +class Circuit; + +/** + * @brief A subcircuit of a circuit + * + * This class essentially is a reference to another circuit + */ +class DB_PUBLIC SubCircuit + : public tl::Object +{ +public: + typedef tl::vector connected_net_list; + + /** + * @brief Default constructor + */ + SubCircuit (); + + /** + * @brief Copy constructor + */ + SubCircuit (const SubCircuit &other); + + /** + * @brief Assignment + */ + SubCircuit &operator= (const SubCircuit &other); + + /** + * @brief Creates a subcircuit reference to the given circuit + */ + SubCircuit (Circuit *circuit_ref, const std::string &name = std::string ()); + + /** + * @brief Destructor + */ + ~SubCircuit (); + + /** + * @brief Gets the subcircuit ID + * The ID is a unique integer which identifies the subcircuit. + * It can be used to retrieve the subcircuit from the circuit using Circuit::subcircuit_by_id. + * When assigned, the subcircuit ID is not 0. + */ + size_t id () const + { + return m_id; + } + + /** + * @brief Gets the circuit the subcircuit lives in (const version) + * This pointer is 0 if the subcircuit isn't added to a circuit + */ + const Circuit *circuit () const + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the subcircuit lives in (non-const version) + * This pointer is 0 if the subcircuit isn't added to a circuit + */ + Circuit *circuit () + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the reference points to (const version) + */ + const Circuit *circuit_ref () const + { + return m_circuit_ref.get (); + } + + /** + * @brief Gets the circuit the reference points to (non-const version) + */ + Circuit *circuit_ref () + { + return m_circuit_ref.get (); + } + + /** + * @brief Sets the name of the subcircuit + * + * The name is one way to identify the subcircuit. The transformation is + * another one. + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name of the subcircuit + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the transformation describing the subcircuit + * + * The transformation is a natural description of a subcircuit + * (in contrast to the name) when deriving it from a layout. + */ + void set_trans (const db::DCplxTrans &trans); + + /** + * @brief Gets the transformation describing the subcircuit + */ + const db::DCplxTrans &trans () const + { + return m_trans; + } + + /** + * @brief Gets the net attached to a specific pin + * Returns 0 if no net is attached. + */ + const Net *net_for_pin (size_t pin_id) const; + + /** + * @brief Gets the net attached to a specific pin (non-const version) + * Returns 0 if no net is attached. + */ + Net *net_for_pin (size_t pin_id) + { + return const_cast (((const SubCircuit *) this)->net_for_pin (pin_id)); + } + + /** + * @brief Connects the given pin to the given net + * If the net is 0 the pin is disconnected. + * If non-null, a NetPinRef object will be inserted into the + * net and connected with the given pin. + */ + void connect_pin (size_t pin_id, Net *net); + +private: + friend class Circuit; + friend class Net; + + tl::weak_ptr m_circuit_ref; + std::string m_name; + db::DCplxTrans m_trans; + std::vector m_pin_refs; + size_t m_id; + Circuit *mp_circuit; + + /** + * @brief Sets the pin reference for a specific pin + */ + void set_pin_ref_for_pin (size_t ppin_id, Net::subcircuit_pin_iterator iter); + + /** + * @brief Sets the circuit reference + */ + void set_circuit_ref (Circuit *c); + + /** + * @brief Sets the circuit the subcircuit belongs to + */ + void set_circuit (Circuit *c) + { + mp_circuit = c; + } + + /** + * @brief Sets the device ID + */ + void set_id (size_t id) + { + m_id = id; + } +}; + +} + +#endif diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 833f42f95..ab1e4df29 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -28,6 +28,8 @@ #include +// ---------------------------------------------------------------------------------------- + static std::string pd2string (const db::DeviceTerminalDefinition &pd) { return pd.name () + "(" + pd.description () + ") #" + tl::to_string (pd.id ()); @@ -38,78 +40,6 @@ static std::string pd2string (const db::DeviceParameterDefinition &pd) return pd.name () + "(" + pd.description () + ")=" + tl::to_string (pd.default_value ()) + " #" + tl::to_string (pd.id ()); } -TEST(1_DeviceTerminalDefinition) -{ - db::DeviceTerminalDefinition pd; - - EXPECT_EQ (pd2string (pd), "() #0"); - pd.set_name ("name"); - pd.set_description ("nothing yet"); - EXPECT_EQ (pd2string (pd), "name(nothing yet) #0"); - - db::DeviceTerminalDefinition pd2; - pd2 = pd; - EXPECT_EQ (pd2string (pd2), "name(nothing yet) #0"); - pd2.set_name ("name2"); - pd2.set_description ("now it has something"); - EXPECT_EQ (pd2string (pd2), "name2(now it has something) #0"); - - db::DeviceClass dc; - dc.add_terminal_definition (pd); - dc.add_terminal_definition (pd2); - EXPECT_EQ (pd2string (dc.terminal_definitions ()[0]), "name(nothing yet) #0"); - EXPECT_EQ (pd2string (dc.terminal_definitions ()[1]), "name2(now it has something) #1"); - - dc.clear_terminal_definitions (); - EXPECT_EQ (dc.terminal_definitions ().empty (), true); - - db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0); - dc.add_parameter_definition (ppd); - - db::DeviceParameterDefinition ppd2 ("P2", "Parameter 2"); - dc.add_parameter_definition (ppd2); - - EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=1 #0"); - EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=0 #1"); - - dc.clear_parameter_definitions (); - EXPECT_EQ (dc.parameter_definitions ().empty (), true); -} - -TEST(2_DeviceClass) -{ - db::DeviceTerminalDefinition pd; - pd.set_name ("name"); - pd.set_description ("nothing yet"); - - db::DeviceTerminalDefinition pd2; - pd2.set_name ("name2"); - pd2.set_description ("now it has something"); - - db::DeviceClass dc; - dc.set_name ("devname"); - dc.set_description ("devdesc"); - EXPECT_EQ (dc.name (), "devname"); - EXPECT_EQ (dc.description (), "devdesc"); - dc.add_terminal_definition (pd); - dc.add_terminal_definition (pd2); - EXPECT_EQ (dc.terminal_definitions ().size (), size_t (2)); - EXPECT_EQ (pd2string (dc.terminal_definitions ()[0]), "name(nothing yet) #0"); - EXPECT_EQ (pd2string (dc.terminal_definitions ()[1]), "name2(now it has something) #1"); - - EXPECT_EQ (pd2string (*dc.terminal_definition (dc.terminal_definitions ()[0].id ())), "name(nothing yet) #0"); - EXPECT_EQ (pd2string (*dc.terminal_definition (dc.terminal_definitions ()[1].id ())), "name2(now it has something) #1"); - EXPECT_EQ (dc.terminal_definition (3), 0); - - db::DeviceClass dc2 = dc; - EXPECT_EQ (dc2.name (), "devname"); - EXPECT_EQ (dc2.description (), "devdesc"); - EXPECT_EQ (dc2.terminal_definitions ().size (), size_t (2)); - EXPECT_EQ (pd2string (*dc2.terminal_definition (dc2.terminal_definitions ()[0].id ())), "name(nothing yet) #0"); - EXPECT_EQ (pd2string (*dc2.terminal_definition (dc2.terminal_definitions ()[1].id ())), "name2(now it has something) #1"); - EXPECT_EQ (dc2.terminal_definition (3), 0); -} - static std::string pins2string (const db::Circuit &c) { std::string res; @@ -123,32 +53,6 @@ static std::string pins2string (const db::Circuit &c) return res; } -TEST(3_CircuitBasic) -{ - db::Circuit c; - c.set_name ("name"); - EXPECT_EQ (c.name (), "name"); - - db::Pin p1 = c.add_pin ("p1"); - db::Pin p2 = c.add_pin ("p2"); - EXPECT_EQ (pins2string (c), "p1#0,p2#1"); - - EXPECT_EQ (c.pin_by_id (0)->name (), "p1"); - EXPECT_EQ (c.pin_by_id (1)->name (), "p2"); - EXPECT_EQ (c.pin_by_id (2), 0); - EXPECT_EQ (c.pin_by_name ("p1")->name (), "p1"); - EXPECT_EQ (c.pin_by_name ("doesnt_exist") == 0, true); - EXPECT_EQ (c.pin_by_name ("p2")->name (), "p2"); - - db::Circuit c2 = c; - EXPECT_EQ (c2.name (), "name"); - EXPECT_EQ (pins2string (c), "p1#0,p2#1"); - - EXPECT_EQ (c2.pin_by_id (0)->name (), "p1"); - EXPECT_EQ (c2.pin_by_id (1)->name (), "p2"); - EXPECT_EQ (c2.pin_by_id (2), 0); -} - static std::string net2string (const db::Net &n) { std::string res; @@ -243,6 +147,186 @@ static std::string netlist2 (const db::Circuit &c) return res; } +static std::string nl2string (const db::Netlist &nl) +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + res += "[" + c->name () + "]\n"; + res += nets2string (*c); + } + return res; +} + +// dual form of netlist +static std::string netlist2 (const db::Netlist &nl) +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + res += netlist2 (*c); + } + return res; +} + +static std::string refs2string (const db::Circuit *c) +{ + std::string res; + for (db::Circuit::const_refs_iterator r = c->begin_refs (); r != c->end_refs (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += r->name (); + } + return res; +} + +static std::string children2string (const db::Circuit *c) +{ + std::string res; + for (db::Circuit::const_child_circuit_iterator r = c->begin_children (); r != c->end_children (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +static std::string parents2string (const db::Circuit *c) +{ + std::string res; + for (db::Circuit::const_parent_circuit_iterator r = c->begin_parents (); r != c->end_parents (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +static std::string td2string (const db::Netlist *nl) +{ + std::string res; + for (db::Netlist::const_top_down_circuit_iterator r = nl->begin_top_down (); r != nl->end_top_down (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +static std::string bu2string (const db::Netlist *nl) +{ + std::string res; + for (db::Netlist::const_bottom_up_circuit_iterator r = nl->begin_bottom_up (); r != nl->end_bottom_up (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +// ---------------------------------------------------------------------------------------- + +TEST(1_DeviceTerminalDefinition) +{ + db::DeviceTerminalDefinition pd; + + EXPECT_EQ (pd2string (pd), "() #0"); + pd.set_name ("name"); + pd.set_description ("nothing yet"); + EXPECT_EQ (pd2string (pd), "name(nothing yet) #0"); + + db::DeviceTerminalDefinition pd2; + pd2 = pd; + EXPECT_EQ (pd2string (pd2), "name(nothing yet) #0"); + pd2.set_name ("name2"); + pd2.set_description ("now it has something"); + EXPECT_EQ (pd2string (pd2), "name2(now it has something) #0"); + + db::DeviceClass dc; + dc.add_terminal_definition (pd); + dc.add_terminal_definition (pd2); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[0]), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[1]), "name2(now it has something) #1"); + + dc.clear_terminal_definitions (); + EXPECT_EQ (dc.terminal_definitions ().empty (), true); + + db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0); + dc.add_parameter_definition (ppd); + + db::DeviceParameterDefinition ppd2 ("P2", "Parameter 2"); + dc.add_parameter_definition (ppd2); + + EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=1 #0"); + EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=0 #1"); + + dc.clear_parameter_definitions (); + EXPECT_EQ (dc.parameter_definitions ().empty (), true); +} + +TEST(2_DeviceClass) +{ + db::DeviceTerminalDefinition pd; + pd.set_name ("name"); + pd.set_description ("nothing yet"); + + db::DeviceTerminalDefinition pd2; + pd2.set_name ("name2"); + pd2.set_description ("now it has something"); + + db::DeviceClass dc; + dc.set_name ("devname"); + dc.set_description ("devdesc"); + EXPECT_EQ (dc.name (), "devname"); + EXPECT_EQ (dc.description (), "devdesc"); + dc.add_terminal_definition (pd); + dc.add_terminal_definition (pd2); + EXPECT_EQ (dc.terminal_definitions ().size (), size_t (2)); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[0]), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[1]), "name2(now it has something) #1"); + + EXPECT_EQ (pd2string (*dc.terminal_definition (dc.terminal_definitions ()[0].id ())), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (*dc.terminal_definition (dc.terminal_definitions ()[1].id ())), "name2(now it has something) #1"); + EXPECT_EQ (dc.terminal_definition (3), 0); + + db::DeviceClass dc2 = dc; + EXPECT_EQ (dc2.name (), "devname"); + EXPECT_EQ (dc2.description (), "devdesc"); + EXPECT_EQ (dc2.terminal_definitions ().size (), size_t (2)); + EXPECT_EQ (pd2string (*dc2.terminal_definition (dc2.terminal_definitions ()[0].id ())), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (*dc2.terminal_definition (dc2.terminal_definitions ()[1].id ())), "name2(now it has something) #1"); + EXPECT_EQ (dc2.terminal_definition (3), 0); +} + +TEST(3_CircuitBasic) +{ + db::Circuit c; + c.set_name ("name"); + EXPECT_EQ (c.name (), "name"); + + db::Pin p1 = c.add_pin ("p1"); + db::Pin p2 = c.add_pin ("p2"); + EXPECT_EQ (pins2string (c), "p1#0,p2#1"); + + EXPECT_EQ (c.pin_by_id (0)->name (), "p1"); + EXPECT_EQ (c.pin_by_id (1)->name (), "p2"); + EXPECT_EQ (c.pin_by_id (2), 0); + EXPECT_EQ (c.pin_by_name ("p1")->name (), "p1"); + EXPECT_EQ (c.pin_by_name ("doesnt_exist") == 0, true); + EXPECT_EQ (c.pin_by_name ("p2")->name (), "p2"); + + db::Circuit c2 = c; + EXPECT_EQ (c2.name (), "name"); + EXPECT_EQ (pins2string (c), "p1#0,p2#1"); + + EXPECT_EQ (c2.pin_by_id (0)->name (), "p1"); + EXPECT_EQ (c2.pin_by_id (1)->name (), "p2"); + EXPECT_EQ (c2.pin_by_id (2), 0); +} + TEST(4_CircuitDevices) { db::DeviceClass dc1; @@ -403,38 +487,6 @@ TEST(4_CircuitDevices) ); } -static std::string nl2string (const db::Netlist &nl) -{ - std::string res; - for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { - res += "[" + c->name () + "]\n"; - res += nets2string (*c); - } - return res; -} - -// dual form of netlist -static std::string netlist2 (const db::Netlist &nl) -{ - std::string res; - for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { - res += netlist2 (*c); - } - return res; -} - -static std::string refs2string (const db::Circuit *c) -{ - std::string res; - for (db::Circuit::const_refs_iterator r = c->begin_refs (); r != c->end_refs (); ++r) { - if (!res.empty ()) { - res += ","; - } - res += r->name (); - } - return res; -} - TEST(4_NetlistSubcircuits) { std::auto_ptr nl (new db::Netlist ()); @@ -897,54 +949,6 @@ TEST(11_NetlistCircuitRefs) EXPECT_EQ (refs2string (c2), "sc1,sc2"); } -static std::string children2string (const db::Circuit *c) -{ - std::string res; - for (db::Circuit::const_child_circuit_iterator r = c->begin_children (); r != c->end_children (); ++r) { - if (!res.empty ()) { - res += ","; - } - res += (*r)->name (); - } - return res; -} - -static std::string parents2string (const db::Circuit *c) -{ - std::string res; - for (db::Circuit::const_parent_circuit_iterator r = c->begin_parents (); r != c->end_parents (); ++r) { - if (!res.empty ()) { - res += ","; - } - res += (*r)->name (); - } - return res; -} - -static std::string td2string (const db::Netlist *nl) -{ - std::string res; - for (db::Netlist::const_top_down_circuit_iterator r = nl->begin_top_down (); r != nl->end_top_down (); ++r) { - if (!res.empty ()) { - res += ","; - } - res += (*r)->name (); - } - return res; -} - -static std::string bu2string (const db::Netlist *nl) -{ - std::string res; - for (db::Netlist::const_bottom_up_circuit_iterator r = nl->begin_bottom_up (); r != nl->end_bottom_up (); ++r) { - if (!res.empty ()) { - res += ","; - } - res += (*r)->name (); - } - return res; -} - TEST(12_NetlistTopology) { std::auto_ptr nl (new db::Netlist ()); From 3b0f4b3d78923129588ee6824e38c2c04421b119 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 12 Jan 2019 00:24:31 +0100 Subject: [PATCH 194/335] WIP: fixed some compiler issues on certain systems. --- src/db/db/dbCircuit.cc | 2 +- src/db/db/dbDevice.cc | 2 +- src/db/db/dbDeviceClass.cc | 2 +- src/db/db/dbFlatRegion.h | 4 ++++ src/db/db/dbHierProcessor.cc | 6 ++++++ src/db/db/dbHierProcessor.h | 7 +++++++ src/db/db/dbNet.cc | 2 +- src/db/db/dbNetlist.cc | 2 +- src/db/db/dbSubCircuit.cc | 2 +- src/db/db/gsiDeclDbNetlist.cc | 2 +- src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 2 +- src/tl/tl/tlList.h | 3 ++- 12 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index e0c85f08c..7acd9c544 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -45,7 +45,7 @@ Circuit::Circuit () } Circuit::Circuit (const Circuit &other) - : mp_netlist (0), + : gsi::ObjectBase (other), tl::Object (other), mp_netlist (0), m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc index dd9b8d8cf..add76bec0 100644 --- a/src/db/db/dbDevice.cc +++ b/src/db/db/dbDevice.cc @@ -52,7 +52,7 @@ Device::Device (DeviceClass *device_class, const std::string &name) } Device::Device (const Device &other) - : mp_device_class (0), m_id (0), mp_circuit (0) + : tl::Object (other), mp_device_class (0), m_id (0), mp_circuit (0) { operator= (other); } diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index 46a2c1a80..e21ff80ea 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -35,7 +35,7 @@ DeviceClass::DeviceClass () } DeviceClass::DeviceClass (const DeviceClass &other) - : mp_netlist (0) + : gsi::ObjectBase (other), tl::Object (other), tl::UniqueId (other), mp_netlist (0) { operator= (other); } diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index c3037080a..d4824fef4 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -114,6 +114,10 @@ public: virtual RegionDelegate *merged_in_place (); virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); virtual RegionDelegate *merged () const; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const + { + return db::AsIfFlatRegion::merged (min_coherence, min_wc); + } virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter); diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 87d95c4b3..c99b49440 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -185,6 +185,12 @@ LocalProcessorCellContext::LocalProcessorCellContext () // .. nothing yet .. } +LocalProcessorCellContext::LocalProcessorCellContext (const LocalProcessorCellContext &other) + : m_propagated (other.m_propagated), m_drops (other.m_drops) +{ + // .. nothing yet .. +} + void LocalProcessorCellContext::add (db::LocalProcessorCellContext *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst) { diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index 2918c4d95..866137bbc 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -141,6 +141,7 @@ public: typedef std::pair parent_inst_type; LocalProcessorCellContext (); + LocalProcessorCellContext (const LocalProcessorCellContext &other); void add (db::LocalProcessorCellContext *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst); void propagate (const std::unordered_set &res); @@ -212,6 +213,12 @@ public: // .. nothing yet .. } + LocalProcessorContexts (const LocalProcessorContexts &other) + : m_contexts_per_cell (other.m_contexts_per_cell), m_subject_layer (other.m_subject_layer), m_intruder_layer (other.m_intruder_layer) + { + // .. nothing yet .. + } + void clear () { m_contexts_per_cell.clear (); diff --git a/src/db/db/dbNet.cc b/src/db/db/dbNet.cc index 20733d442..69f085817 100644 --- a/src/db/db/dbNet.cc +++ b/src/db/db/dbNet.cc @@ -167,7 +167,7 @@ Net::Net (const std::string &name) } Net::Net (const Net &other) - : m_cluster_id (0), mp_circuit (0) + : tl::Object (other), m_cluster_id (0), mp_circuit (0) { operator= (other); } diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 21dced73c..8a5135d05 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -40,7 +40,7 @@ Netlist::Netlist () } Netlist::Netlist (const Netlist &other) - : m_valid_topology (false), m_lock_count (0), + : gsi::ObjectBase (other), tl::Object (other), m_valid_topology (false), m_lock_count (0), m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits) { diff --git a/src/db/db/dbSubCircuit.cc b/src/db/db/dbSubCircuit.cc index 8e6407326..9de235b97 100644 --- a/src/db/db/dbSubCircuit.cc +++ b/src/db/db/dbSubCircuit.cc @@ -51,7 +51,7 @@ SubCircuit::SubCircuit (Circuit *circuit, const std::string &name) } SubCircuit::SubCircuit (const SubCircuit &other) - : m_id (0), mp_circuit (0) + : tl::Object (other), m_id (0), mp_circuit (0) { operator= (other); } diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 3d0bf45e7..c088750cd 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -509,7 +509,7 @@ public: virtual bool combine_devices (db::Device *a, db::Device *b) const { if (cb_combine_devices.can_issue ()) { - return cb_combine_devices.issue (&db::DeviceClass::combine_devices, a, b); + return cb_combine_devices.issue (&db::DeviceClass::combine_devices, a, b); } else { return db::DeviceClass::combine_devices (a, b); } diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index a201164a8..7ccacac19 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -80,7 +80,7 @@ public: virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector &layers) const { if (cb_get_connectivity.can_issue ()) { - return cb_get_connectivity.issue &> (&GenericDeviceExtractor::get_connectivity_fb, layout, layers); + return cb_get_connectivity.issue &> (&GenericDeviceExtractor::get_connectivity_fb, layout, layers); } else { return db::NetlistDeviceExtractor::get_connectivity (layout, layers); } diff --git a/src/tl/tl/tlList.h b/src/tl/tl/tlList.h index 3d38dc95b..2d09e868e 100644 --- a/src/tl/tl/tlList.h +++ b/src/tl/tl/tlList.h @@ -303,6 +303,7 @@ public: list_impl () { } list_impl (const list_impl &other) + : list_impl (other) { operator= (other); } @@ -522,6 +523,6 @@ public: } }; -}; +} #endif From 1af81b74d20a8e082e72200b68bb0a9075d0f1ed Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 14 Jan 2019 00:59:47 +0100 Subject: [PATCH 195/335] WIP: refactoring - turning devices into cells for better backannotation. --- src/db/db/db.pro | 8 +- src/db/db/dbDeepShapeStore.cc | 22 +- src/db/db/dbDeepShapeStore.h | 1 + src/db/db/dbDevice.cc | 33 ++- src/db/db/dbDevice.h | 47 +++ src/db/db/dbHierNetworkProcessor.cc | 5 + src/db/db/dbLayoutToNetlist.cc | 37 ++- src/db/db/dbLayoutToNetlist.h | 15 +- src/db/db/dbLayoutToNetlistReader.cc | 30 ++ src/db/db/dbLayoutToNetlistReader.h | 35 +++ src/db/db/dbLayoutToNetlistWriter.cc | 295 +++++++++++++++++++ src/db/db/dbLayoutToNetlistWriter.h | 72 +++++ src/db/db/dbNetlistDeviceExtractor.cc | 159 ++++++++-- src/db/db/dbNetlistDeviceExtractor.h | 65 +++- src/db/db/dbNetlistDeviceExtractorClasses.cc | 2 + src/db/db/dbNetlistExtractor.cc | 195 +++++++++--- src/db/db/dbNetlistExtractor.h | 52 +++- src/db/unit_tests/dbNetlistExtractorTests.cc | 116 +++----- testdata/algo/device_extract_au1.gds | Bin 9014 -> 7918 bytes 19 files changed, 992 insertions(+), 197 deletions(-) create mode 100644 src/db/db/dbLayoutToNetlistReader.cc create mode 100644 src/db/db/dbLayoutToNetlistReader.h create mode 100644 src/db/db/dbLayoutToNetlistWriter.cc create mode 100644 src/db/db/dbLayoutToNetlistWriter.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 7a30f7331..0da3ca0ca 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -159,7 +159,9 @@ SOURCES = \ dbDeviceClass.cc \ dbNet.cc \ dbSubCircuit.cc \ - dbPin.cc + dbPin.cc \ + dbLayoutToNetlistReader.cc \ + dbLayoutToNetlistWriter.cc HEADERS = \ dbArray.h \ @@ -283,7 +285,9 @@ HEADERS = \ dbDevice.h \ dbDeviceClass.h \ dbPin.h \ - dbSubCircuit.h + dbSubCircuit.h \ + dbLayoutToNetlistReader.h \ + dbLayoutToNetlistWriter.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 70f23be24..b677bcfff 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -340,7 +340,10 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator layout_index = (unsigned int) m_layouts.size (); m_layouts.push_back (new LayoutHolder ()); - m_layouts.back ()->layout.dbu (si.layout ()->dbu ()); + + db::Layout &layout = m_layouts.back ()->layout; + layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier); + layout.dbu (si.layout ()->dbu ()); m_layout_map[si] = layout_index; @@ -378,6 +381,12 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator return DeepLayer (this, layout_index, layer_index); } +void +DeepShapeStore::invalidate_hier () +{ + m_delivery_mapping_cache.clear (); +} + const db::CellMapping & DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell) { @@ -409,8 +418,6 @@ DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_ // create from them. We need to consider however, that the hierarchy builder is allowed to create // variants which we cannot map. - bool any_skipped = false; - for (HierarchyBuilder::cell_map_type::const_iterator m = original_builder.begin_cell_map (); m != original_builder.end_cell_map (); ++m) { HierarchyBuilder::cell_map_type::const_iterator mm = m; @@ -425,16 +432,13 @@ DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_ if (! skip) { cm->second.map (m->first.first, m->second); - } else { - any_skipped = true; } } - if (any_skipped) { - // Add new cells for the variants - cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top); - } + // Add new cells for the variants and (possible) devices which are cells added during the device + // extraction process + cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top); } else if (into_layout->cells () == 1) { diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 89d109cdd..0c658eb17 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -420,6 +420,7 @@ private: struct LayoutHolder; + void invalidate_hier (); void add_ref (unsigned int layout, unsigned int layer); void remove_ref (unsigned int layout, unsigned int layer); diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc index add76bec0..8e0f6ce54 100644 --- a/src/db/db/dbDevice.cc +++ b/src/db/db/dbDevice.cc @@ -31,7 +31,7 @@ namespace db // Device class implementation Device::Device () - : mp_device_class (0), m_id (0), mp_circuit (0) + : mp_device_class (0), m_cell_index (std::numeric_limits::max ()), m_id (0), mp_circuit (0) { // .. nothing yet .. } @@ -46,13 +46,13 @@ Device::~Device () } Device::Device (DeviceClass *device_class, const std::string &name) - : mp_device_class (device_class), m_name (name), m_id (0), mp_circuit (0) + : mp_device_class (device_class), m_name (name), m_cell_index (std::numeric_limits::max ()), m_id (0), mp_circuit (0) { // .. nothing yet .. } Device::Device (const Device &other) - : tl::Object (other), mp_device_class (0), m_id (0), mp_circuit (0) + : tl::Object (other), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), m_id (0), mp_circuit (0) { operator= (other); } @@ -61,6 +61,10 @@ Device &Device::operator= (const Device &other) { if (this != &other) { m_name = other.m_name; + m_position = other.m_position; + m_cell_index = other.m_cell_index; + m_terminal_cluster_ids = other.m_terminal_cluster_ids; + m_parameters = other.m_parameters; mp_device_class = other.mp_device_class; } return *this; @@ -79,6 +83,29 @@ void Device::set_name (const std::string &n) } } +void Device::set_position (const db::DPoint &pt) +{ + m_position = pt; +} + +void Device::set_cell_index (db::cell_index_type ci) +{ + m_cell_index = ci; +} + +size_t Device::cluster_id_for_terminal (size_t terminal_id) const +{ + return terminal_id < m_terminal_cluster_ids.size () ? m_terminal_cluster_ids [terminal_id] : 0; +} + +void Device::set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id) +{ + if (m_terminal_cluster_ids.size () <= terminal_id) { + m_terminal_cluster_ids.resize (terminal_id + 1, 0); + } + m_terminal_cluster_ids [terminal_id] = cluster_id; +} + void Device::set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter) { if (m_terminal_refs.size () < terminal_id + 1) { diff --git a/src/db/db/dbDevice.h b/src/db/db/dbDevice.h index c918cbe35..8074e66fb 100644 --- a/src/db/db/dbDevice.h +++ b/src/db/db/dbDevice.h @@ -25,6 +25,7 @@ #include "dbCommon.h" #include "dbNet.h" +#include "dbPoint.h" #include "tlObject.h" @@ -127,6 +128,49 @@ public: return m_name; } + /** + * @brief Sets the device position + * The device position should be the center of the recognition shape or something similar. + * Giving the device a position allows combining multiple devices with the same + * relative geometry into a single cell. + * The position has to be given in micrometer units. + */ + void set_position (const db::DPoint &pos); + + /** + * @brief Gets the device position + */ + const db::DPoint &position () const + { + return m_position; + } + + /** + * @brief Sets the device cell index + * In the layout, a device is represented by a cell. This attribute gives the index of this + * cell. + */ + void set_cell_index (db::cell_index_type ci); + + /** + * @brief Gets the device cell index + */ + db::cell_index_type cell_index () const + { + return m_cell_index; + } + + /** + * @brief Gets the cluster ID for a given terminal + * This attribute connects the device terminal with a terminal cluster + */ + size_t cluster_id_for_terminal (size_t terminal_id) const; + + /** + * @brief Sets the cluster ID for a given terminal + */ + void set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id); + /** * @brief Gets the net attached to a specific terminal * Returns 0 if no net is attached. @@ -180,7 +224,10 @@ private: DeviceClass *mp_device_class; std::string m_name; + db::DPoint m_position; + db::cell_index_type m_cell_index; std::vector m_terminal_refs; + std::vector m_terminal_cluster_ids; std::vector m_parameters; size_t m_id; Circuit *mp_circuit; diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 71b74530b..2f41ea28e 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1664,6 +1664,11 @@ template void hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) { + if (m_per_cell_clusters.find (cell.cell_index ()) != m_per_cell_clusters.end ()) { + // skip pre-build clusters (from devices) + return; + } + std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); if (tl::verbosity () >= 40) { tl::log << msg; diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index c70fcef69..57896e388 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -110,7 +110,7 @@ void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, co if (! mp_netlist.get ()) { mp_netlist.reset (new db::Netlist ()); } - extractor.extract(m_dss, layers, mp_netlist.get ()); + extractor.extract(m_dss, layers, *mp_netlist, m_net_clusters); } void LayoutToNetlist::connect (const db::Region &l) @@ -183,7 +183,12 @@ void LayoutToNetlist::extract_netlist () if (! mp_netlist.get ()) { mp_netlist.reset (new db::Netlist ()); } - m_netex.extract_nets(m_dss, m_conn, mp_netlist.get ()); + + m_net_clusters.clear (); + + db::NetlistExtractor netex; + netex.extract_nets(m_dss, m_conn, *mp_netlist, m_net_clusters); + m_netlist_extracted = true; } @@ -227,14 +232,6 @@ db::Netlist *LayoutToNetlist::netlist () const return mp_netlist.get (); } -const db::hier_clusters &LayoutToNetlist::net_clusters () const -{ - if (! m_netlist_extracted) { - throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); - } - return m_netex.clusters (); -} - template static void deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr) { @@ -261,26 +258,26 @@ static void deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const T } template -static void deliver_shapes_of_net_recursive (const db::NetlistExtractor &netex, const db::Net &net, unsigned int layer_id, To &to) +static void deliver_shapes_of_net_recursive (const db::hier_clusters &clusters, const db::Net &net, unsigned int layer_id, To &to) { const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); db::cell_index_type ci = circuit->cell_index (); - for (db::recursive_cluster_shape_iterator rci (netex.clusters (), layer_id, ci, net.cluster_id ()); !rci.at_end (); ++rci) { + for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, net.cluster_id ()); !rci.at_end (); ++rci) { deliver_shape (*rci, to, rci.trans ()); } } template -static void deliver_shapes_of_net_nonrecursive (const db::NetlistExtractor &netex, const db::Net &net, unsigned int layer_id, To &to) +static void deliver_shapes_of_net_nonrecursive (const db::hier_clusters &clusters, const db::Net &net, unsigned int layer_id, To &to) { const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); db::cell_index_type ci = circuit->cell_index (); - const db::local_cluster &lc = netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); + const db::local_cluster &lc = clusters.clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); for (db::local_cluster::shape_iterator s = lc.begin (layer_id); !s.at_end (); ++s) { deliver_shape (*s, to, db::UnitTrans ()); @@ -292,9 +289,9 @@ void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_la unsigned int lid = layer_of (of_layer); if (! recursive) { - deliver_shapes_of_net_nonrecursive (m_netex, net, lid, to); + deliver_shapes_of_net_nonrecursive (m_net_clusters, net, lid, to); } else { - deliver_shapes_of_net_recursive (m_netex, net, lid, to); + deliver_shapes_of_net_recursive (m_net_clusters, net, lid, to); } } @@ -304,9 +301,9 @@ db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region std::auto_ptr res (new db::Region ()); if (! recursive) { - deliver_shapes_of_net_nonrecursive (m_netex, net, lid, *res); + deliver_shapes_of_net_nonrecursive (m_net_clusters, net, lid, *res); } else { - deliver_shapes_of_net_recursive (m_netex, net, lid, *res); + deliver_shapes_of_net_recursive (m_net_clusters, net, lid, *res); } return res.release (); @@ -326,7 +323,7 @@ LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); - const db::connected_clusters &clusters = m_netex.clusters ().clusters_per_cell (circuit->cell_index ()); + const db::connected_clusters &clusters = m_net_clusters.clusters_per_cell (circuit->cell_index ()); typedef db::connected_clusters::connections_type connections_type; const connections_type &connections = clusters.connections_for_cluster (net.cluster_id ()); for (connections_type::const_iterator c = connections.begin (); c != connections.end (); ++c) { @@ -390,7 +387,7 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target continue; } - const db::connected_clusters &ccl = m_netex.clusters ().clusters_per_cell (c->cell_index ()); + const db::connected_clusters &ccl = m_net_clusters.clusters_per_cell (c->cell_index ()); const db::local_cluster &cl = ccl.cluster_by_id (n->cluster_id ()); bool any_connections = ! ccl.connections_for_cluster (n->cluster_id ()).empty (); diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 4668bb9e6..af30a3436 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -197,6 +197,14 @@ public: */ const db::Cell *internal_top_cell () const; + /** + * @brief Gets the connectivity object + */ + const db::Connectivity &connectivity () const + { + return m_conn; + } + /** * @brief Gets the internal layer for a given extraction layer * This method is required to derive the internal layer index - for example for @@ -227,7 +235,10 @@ public: * NOTE: the layer and cell indexes used inside this structure refer to the * internal layout. */ - const db::hier_clusters &net_clusters () const; + const db::hier_clusters &net_clusters () const + { + return m_net_clusters; + } /** * @brief Returns all shapes of a specific net and layer. @@ -327,7 +338,7 @@ private: db::RecursiveShapeIterator m_iter; db::DeepShapeStore m_dss; db::Connectivity m_conn; - db::NetlistExtractor m_netex; + db::hier_clusters m_net_clusters; std::auto_ptr mp_netlist; std::set m_dlrefs; bool m_netlist_extracted; diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc new file mode 100644 index 000000000..192a6057d --- /dev/null +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -0,0 +1,30 @@ + +/* + + 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 "dbLayoutToNetlistReader.h" + +namespace db +{ + + + +} diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h new file mode 100644 index 000000000..038820ac2 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -0,0 +1,35 @@ + +/* + + 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_dbLayoutToNetlistReader +#define HDR_dbLayoutToNetlistReader + +#include "dbCommon.h" + +namespace db { + +// ... + +} + +#endif + diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc new file mode 100644 index 000000000..8bad67fe8 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -0,0 +1,295 @@ + +/* + + 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 "dbLayoutToNetlistWriter.h" +#include "dbLayoutToNetlist.h" + +namespace db +{ + + +// ------------------------------------------------------------------------------------------- +// LayoutToNetlistStandardWriter implementation + +/** + * Comments are introduced by hash: # ... + * Names are words (alphanumerical plus "$", "_", ".") or enclosed in single or double quotes. + * Escape character is backslash. + * Separator is either , or whitespace. Keywords and names are case sensitive. + * Short keys are provided for compacter representation. Short keys can be + * non-alpha (e.g. "*") or empty. + * Single-valued attributes can be given without brackets. + * All dimensions are in units of database unit. + * + * Global statements: + * + * description() - an arbitrary description text + * dbu() - specifies the database unit [short key: D] + * top() - specifies the name of the top circuit [short key: T] + * layer() - define a layer [short key: L] + * connect( ...) - connects layer1 with the following layers [short key: C] + * global( ...) - connects a layer with the given global nets [short key: G] + * circuit( ...) - defines a circuit (cell) [short key: X] + * + * Inside the circuit: + * + * net( ...) - specifies net geometry [short key: N] + * device( ...) - defines a device [short key: D] + * subcircuit( ...) - defines a subcircuit [short key: X] + * + * Inside a net: + * + * polygon( ...) - defines a polygon [short key: P] + * rect( ) + * - defines a rectangle [short key: R] + * + * Inside a device: + * + * param( ) - defines a parameter [short key P] + * terminal( ) + * - specifies connection of the terminal with + * a net (short key: empty) + * location( ) - location of the device [short key L] + * + * Inside a subcircuit: + * + * location( ) - location of the subcircuit [short key L] + * rotation() - rotation angle [short key O] + * mirror - if specified, the instance is mirrored before rotation [short key M] + * scale() - magnification [short key *] + * pin( ) - specifies connection of the pin with a net + */ + +static std::string description_key ("description"); +static std::string top_key ("top"); +static std::string dbu_key ("dbu"); +static std::string layer_key ("layer"); +static std::string text_key ("text"); +static std::string connect_key ("connect"); +static std::string global_key ("global"); +static std::string circuit_key ("circuit"); +static std::string net_key ("net"); +static std::string device_key ("device"); +static std::string subcircuit_key ("subcircuit"); +static std::string polygon_key ("polygon"); +static std::string rect_key ("rect"); +static std::string terminal_key ("terminal"); +static std::string label_key ("label"); +static std::string param_key ("param"); +static std::string location_key ("location"); +static std::string rotation_key ("rotation"); +static std::string mirror_key ("mirror"); +static std::string scale_key ("scale"); +static std::string pin_key ("pin"); +static std::string indent1 (" "); +static std::string indent2 (" "); +static std::string endl ("\n"); + +LayoutToNetlistStandardWriter::LayoutToNetlistStandardWriter (tl::OutputStream &stream) + : mp_stream (&stream) +{ + // .. nothing yet .. +} + +static std::string name_for_layer (const db::Layout *layout, unsigned int l) +{ + const db::LayerProperties &lp = layout->get_properties (l); + if (lp.is_named ()) { + return tl::to_word_or_quoted_string (lp.name); + } else { + return "L" + tl::to_string (l); + } +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) +{ + const db::Layout *ly = l2n->internal_layout (); + const db::Netlist *nl = l2n->netlist (); + + *mp_stream << top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; + *mp_stream << dbu_key << "(" << ly->dbu () << ")" << endl; + + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { + + *mp_stream << layer_key << "(" << name_for_layer (ly, *l) << ")" << endl; + + db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l); + db::Connectivity::layer_iterator cb = l2n->connectivity ().begin_connected (*l); + if (cb != ce) { + *mp_stream << connect_key << "(" << name_for_layer (ly, *l); + for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) { + *mp_stream << " " << name_for_layer (ly, *l); + } + *mp_stream << ")" << endl; + } + + db::Connectivity::global_nets_iterator ge = l2n->connectivity ().end_global_connections (*l); + db::Connectivity::global_nets_iterator gb = l2n->connectivity ().begin_global_connections (*l); + if (gb != ge) { + *mp_stream << global_key << "(" << name_for_layer (ly, *l); + for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) { + *mp_stream << " " << tl::to_word_or_quoted_string (l2n->connectivity ().global_net_name (*g)); + } + *mp_stream << ")" << endl; + } + + } + + for (db::Netlist::const_circuit_iterator x = nl->begin_circuits (); x != nl->end_circuits (); ++x) { + *mp_stream << circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; + write (l2n, *x); + *mp_stream << ")" << endl; + } +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit) +{ + for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + write (l2n, *n); + } + + for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { + write (l2n, *d); + } + + for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) { + write (l2n, *x); + } +} + +template +void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr) +{ + for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) { + typename T::point_type pt = tr * *c; + stream << " " << pt.x () << " " << pt.y (); + } +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Net &net) +{ + const db::Layout *ly = l2n->internal_layout (); + const db::hier_clusters &clusters = l2n->net_clusters (); + const db::Circuit *circuit = net.circuit (); + const db::Connectivity &conn = l2n->connectivity (); + + *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + + const db::local_cluster &lc = clusters.clusters_per_cell (circuit->cell_index ()).cluster_by_id (net.cluster_id ()); + for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { + + *mp_stream << indent2; + + const db::Polygon &poly = s->obj (); + if (poly.is_box ()) { + + db::Box box = s->trans () * poly.box (); + *mp_stream << rect_key << "(" << name_for_layer (ly, *l); + *mp_stream << " " << box.left () << " " << box.bottom (); + *mp_stream << " " << box.right () << " " << box.top (); + *mp_stream << ")"; + + } else { + + *mp_stream << polygon_key << "(" << name_for_layer (ly, *l); + if (poly.holes () > 0) { + db::SimplePolygon sp (poly); + write_points (*mp_stream, sp, s->trans ()); + } else { + write_points (*mp_stream, poly, s->trans ()); + } + *mp_stream << ")"; + + } + + } + + } + + *mp_stream << indent1 << ")" << endl; +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, const db::SubCircuit &subcircuit) +{ + *mp_stream << indent1 << subcircuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.name ()); + + const db::DCplxTrans &tr = subcircuit.trans (); + if (tr.is_mag ()) { + *mp_stream << " " << scale_key << "(" << tr.mag () << ")"; + } + if (tr.is_mirror ()) { + *mp_stream << " " << mirror_key; + } + if (fabs (tr.angle ()) > 1e-6) { + *mp_stream << " " << rotation_key << "(" << tr.angle () << ")"; + } + *mp_stream << " " << location_key << "(" << tr.disp ().x () << " " << tr.disp ().y () << ")"; + + // each pin in one line for more than 16 pins + bool separate_lines = (subcircuit.circuit ()->pin_count () > 16); + + if (separate_lines) { + *mp_stream << endl; + } + + for (db::Circuit::const_pin_iterator p = subcircuit.circuit ()->begin_pins (); p != subcircuit.circuit ()->end_pins (); ++p) { + if (separate_lines) { + *mp_stream << indent2; + } else { + *mp_stream << " "; + } + *mp_stream << pin_key << "(" << tl::to_word_or_quoted_string (p->name ()) << " " << tl::to_word_or_quoted_string (subcircuit.net_for_pin (p->id ())->expanded_name ()) << ")"; + if (separate_lines) { + *mp_stream << endl; + } + } + + if (separate_lines) { + *mp_stream << indent1; + } + + *mp_stream << ")" << endl; +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, const db::Device &device) +{ + // @@@ TODO: add location + + *mp_stream << indent1 << device_key << "(" << tl::to_word_or_quoted_string (device.name ()); + *mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()); + + const std::vector &pd = device.device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + *mp_stream << " " << param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")"; + } + + const std::vector &td = device.device_class ()->terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + *mp_stream << " " << terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (device.net_for_terminal (i->id ())->expanded_name ()) << ")"; + } + + *mp_stream << ")" << endl; +} + +} diff --git a/src/db/db/dbLayoutToNetlistWriter.h b/src/db/db/dbLayoutToNetlistWriter.h new file mode 100644 index 000000000..f158f8911 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistWriter.h @@ -0,0 +1,72 @@ + +/* + + 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_dbLayoutToNetlistWriter +#define HDR_dbLayoutToNetlistWriter + +#include "dbCommon.h" +#include "tlStream.h" + +namespace db +{ + +class LayoutToNetlist; +class Net; +class Circuit; +class SubCircuit; +class Device; + +/** + * @brief The base class for a LayoutToNetlist writer + */ +class DB_PUBLIC LayoutToNetlistWriterBase +{ +public: + LayoutToNetlistWriterBase () { } + virtual ~LayoutToNetlistWriterBase () { } + + virtual void write (const db::LayoutToNetlist *l2n) = 0; +}; + +/** + * @brief The standard writer + */ +class DB_PUBLIC LayoutToNetlistStandardWriter + : public LayoutToNetlistWriterBase +{ +public: + LayoutToNetlistStandardWriter (tl::OutputStream &stream); + + void write (const db::LayoutToNetlist *l2n); + +private: + tl::OutputStream *mp_stream; + + void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit); + void write (const db::LayoutToNetlist *l2n, const db::Net &net); + void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit); + void write (const db::LayoutToNetlist *l2n, const db::Device &device); +}; + +} + +#endif diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 16bac404e..f8c44f331 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -50,7 +50,9 @@ NetlistDeviceExtractor::NetlistDeviceExtractor (const std::string &name) : mp_layout (0), m_cell_index (0), mp_circuit (0) { m_name = name; - m_propname_id = 0; + m_terminal_id_propname_id = 0; + m_device_class_propname_id = 0; + m_device_id_propname_id = 0; } NetlistDeviceExtractor::~NetlistDeviceExtractor () @@ -58,9 +60,21 @@ NetlistDeviceExtractor::~NetlistDeviceExtractor () // .. nothing yet .. } -const tl::Variant &NetlistDeviceExtractor::terminal_property_name () +const tl::Variant &NetlistDeviceExtractor::terminal_id_property_name () { - static tl::Variant name ("TERMINAL"); + static tl::Variant name ("TERMINAL_ID"); + return name; +} + +const tl::Variant &NetlistDeviceExtractor::device_id_property_name () +{ + static tl::Variant name ("DEVICE_ID"); + return name; +} + +const tl::Variant &NetlistDeviceExtractor::device_class_property_name () +{ + static tl::Variant name ("DEVICE_CLASS"); return name; } @@ -68,7 +82,9 @@ void NetlistDeviceExtractor::initialize (db::Netlist *nl) { m_layer_definitions.clear (); mp_device_class = 0; - m_propname_id = 0; + m_terminal_id_propname_id = 0; + m_device_id_propname_id = 0; + m_device_class_propname_id = 0; m_netlist.reset (nl); setup (); @@ -79,9 +95,9 @@ static void insert_into_region (const db::PolygonRef &s, const db::ICplxTrans &t region.insert (s.obj ().transformed (tr * db::ICplxTrans (s.trans ()))); } -void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDeviceExtractor::input_layers &layer_map, db::Netlist *nl) +void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDeviceExtractor::input_layers &layer_map, db::Netlist &nl, hier_clusters_type &clusters) { - initialize (nl); + initialize (&nl); std::vector layers; layers.reserve (m_layer_definitions.size ()); @@ -107,16 +123,16 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDevi } - extract_without_initialize (dss.layout (), dss.initial_cell (), layers); + extract_without_initialize (dss.layout (), dss.initial_cell (), clusters, layers); } -void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl) +void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl, hier_clusters_type &clusters) { initialize (nl); - extract_without_initialize (layout, cell, layers); + extract_without_initialize (layout, cell, clusters, layers); } -void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector &layers) +void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector &layers) { tl_assert (layers.size () == m_layer_definitions.size ()); @@ -125,9 +141,12 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: mp_layout = &layout; m_layers = layers; + mp_clusters = &clusters; // terminal properties are kept in a property with the terminal_property_name name - m_propname_id = mp_layout->properties_repository ().prop_name_id (terminal_property_name ()); + m_terminal_id_propname_id = mp_layout->properties_repository ().prop_name_id (terminal_id_property_name ()); + m_device_id_propname_id = mp_layout->properties_repository ().prop_name_id (device_id_property_name ()); + m_device_class_propname_id = mp_layout->properties_repository ().prop_name_id (device_class_property_name ()); tl_assert (m_netlist.get () != 0); @@ -150,6 +169,11 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: // for each cell investigate the clusters for (std::set::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) { + // skip device cells from previous extractions + if (is_device_cell (*ci)) { + continue; + } + m_cell_index = *ci; std::map::const_iterator c2c = circuits_by_cell.find (*ci); @@ -192,11 +216,116 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: // do the actual device extraction extract_devices (layer_geometry); + // push the new devices to the layout + push_new_devices (); + } } } +bool NetlistDeviceExtractor::is_device_cell (db::cell_index_type ci) const +{ + db::properties_id_type pi = mp_layout->cell (ci).prop_id (); + if (pi == 0) { + return false; + } + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (pi); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_device_class_propname_id) { + return true; + } + } + + return false; +} + +void NetlistDeviceExtractor::push_new_devices () +{ + db::VCplxTrans dbu_inv = db::CplxTrans (mp_layout->dbu ()).inverted (); + + for (std::map::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { + + db::Vector disp = dbu_inv * d->first->position () - db::Point (); + + DeviceCellKey key; + + for (geometry_per_terminal_type::const_iterator t = d->second.begin (); t != d->second.end (); ++t) { + std::map > > = key.geometry [t->first]; + for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) { + std::set &gl = gt [l->first]; + for (std::vector::const_iterator p = l->second.begin (); p != l->second.end (); ++p) { + db::PolygonRef pr = *p; + pr.transform (db::PolygonRef::trans_type (-disp)); + gl.insert (pr); + } + } + } + + const std::vector &pd = mp_device_class->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + key.parameters.insert (std::make_pair (p->id (), d->first->parameter_value (p->id ()))); + } + + db::PropertiesRepository::properties_set ps; + + std::map::iterator c = m_device_cells.find (key); + if (c == m_device_cells.end ()) { + + std::string cell_name = "D$" + mp_device_class->name (); + db::Cell &device_cell = mp_layout->cell (mp_layout->add_cell (cell_name.c_str ())); + c = m_device_cells.insert (std::make_pair (key, device_cell.cell_index ())).first; + + // attach the device class ID to the cell + ps.clear (); + ps.insert (std::make_pair (m_device_class_propname_id, tl::Variant (mp_device_class->name ()))); + device_cell.prop_id (mp_layout->properties_repository ().properties_id (ps)); + + db::connected_clusters &cc = mp_clusters->clusters_per_cell (device_cell.cell_index ()); + + for (geometry_per_terminal_type::const_iterator t = d->second.begin (); t != d->second.end (); ++t) { + + // Build a property set for the device terminal ID + ps.clear (); + ps.insert (std::make_pair (m_terminal_id_propname_id, tl::Variant (t->first))); + db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); + + // initialize the local cluster (will not be extracted) + db::local_cluster *lc = cc.insert (); + lc->add_attr (pi); + + // build the cell shapes and local cluster + for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) { + db::Shapes &shapes = device_cell.shapes (l->first); + for (std::vector::const_iterator s = l->second.begin (); s != l->second.end (); ++s) { + db::PolygonRef pr = *s; + pr.transform (db::PolygonRef::trans_type (-disp)); + shapes.insert (db::PolygonRefWithProperties (pr, pi)); + lc->add (*s, l->first); + } + } + + } + + } + + // make the cell index known to the device + d->first->set_cell_index (c->second); + + // Build a property set for the device ID + ps.clear (); + ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (d->first->id ()))); + db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); + + db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (c->second), db::Trans (disp)), pi); + mp_layout->cell (m_cell_index).insert (inst); + + } + + m_new_devices.clear (); +} + void NetlistDeviceExtractor::setup () { // .. the default implementation does nothing .. @@ -253,14 +382,8 @@ void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id tl_assert (geometry_index < m_layers.size ()); unsigned int layer_index = m_layers [geometry_index]; - // Build a property set for the DeviceTerminalProperty - db::PropertiesRepository::properties_set ps; - tl::Variant &v = ps.insert (std::make_pair (m_propname_id, tl::Variant ()))->second; - v = tl::Variant (new db::DeviceTerminalProperty (device->id (), terminal_id), db::NetlistProperty::variant_class (), true); - db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); - db::PolygonRef pr (polygon, mp_layout->shape_repository ()); - mp_layout->cell (m_cell_index).shapes (layer_index).insert (db::PolygonRefWithProperties (pr, pi)); + m_new_devices[device][terminal_id][layer_index].push_back (pr); } void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Box &box) diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index a2a845eb5..bf6281919 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -194,6 +194,7 @@ public: typedef std::vector layer_definitions; typedef layer_definitions::const_iterator layer_definitions_iterator; typedef std::map input_layers; + typedef db::hier_clusters hier_clusters_type; /** * @brief Constructor @@ -217,11 +218,21 @@ public: /** * @brief Gets the property name for the device terminal annotation - * Annotation happens through db::DeviceTerminalProperty objects attached to - * the terminal shapes. - * The name used for the property is the one returned by this function. + * This name is used to attach the terminal ID to terminal shapes. */ - static const tl::Variant &terminal_property_name (); + static const tl::Variant &terminal_id_property_name (); + + /** + * @brief Gets the property name for the device id annotation + * This name is used to attach the device ID to instances. + */ + static const tl::Variant &device_id_property_name (); + + /** + * @brief Gets the property name for the device class annotation + * This name is used to attach the device class name to cells. + */ + static const tl::Variant &device_class_property_name (); /** * @brief Performs the extraction @@ -241,7 +252,7 @@ public: * * NOTE: The extractor expects "PolygonRef" type layers. */ - void extract (Layout &layout, Cell &cell, const std::vector &layers, Netlist *netlist); + void extract (Layout &layout, Cell &cell, const std::vector &layers, Netlist *netlist, hier_clusters_type &clusters); /** * @brief Extracts the devices from a list of regions @@ -250,7 +261,7 @@ public: * named regions for input. These regions need to be of deep region type and * originate from the same layout than the DeepShapeStore. */ - void extract (DeepShapeStore &dss, const input_layers &layers, Netlist *netlist); + void extract (DeepShapeStore &dss, const input_layers &layers, Netlist &netlist, hier_clusters_type &clusters); /** * @brief Gets the error iterator, begin @@ -452,9 +463,43 @@ protected: std::string cell_name () const; private: + struct DeviceCellKey + { + DeviceCellKey () { } + + bool operator== (const DeviceCellKey &other) const + { + if (geometry != other.geometry) { + return false; + } + if (parameters != other.parameters) { + return false; + } + return true; + } + + bool operator< (const DeviceCellKey &other) const + { + if (geometry != other.geometry) { + return geometry < other.geometry; + } + if (parameters != other.parameters) { + return parameters < other.parameters; + } + return false; + } + + std::map > > geometry; + std::map parameters; + }; + + typedef std::map > geometry_per_layer_type; + typedef std::map geometry_per_terminal_type; + tl::weak_ptr m_netlist; db::Layout *mp_layout; - db::properties_id_type m_propname_id; + db::properties_id_type m_terminal_id_propname_id, m_device_id_propname_id, m_device_class_propname_id; + hier_clusters_type *mp_clusters; db::cell_index_type m_cell_index; db::Circuit *mp_circuit; db::DeviceClass *mp_device_class; @@ -462,6 +507,8 @@ private: layer_definitions m_layer_definitions; std::vector m_layers; error_list m_errors; + std::map m_new_devices; + std::map m_device_cells; // no copying NetlistDeviceExtractor (const NetlistDeviceExtractor &); @@ -473,7 +520,9 @@ private: */ void initialize (db::Netlist *nl); - void extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector &layers); + void extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector &layers); + void push_new_devices (); + bool is_device_cell (db::cell_index_type ci) const; }; } diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index 712ae0b9f..79f344f94 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -98,6 +98,8 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vectorset_position (db::CplxTrans (dbu ()) * p->box ().center ()); + device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, dbu () * edges.length () * 0.5); device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, dbu () * (p->perimeter () - edges.length ()) * 0.5); diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 538e46d39..de0e4c118 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -29,45 +29,50 @@ namespace db { NetlistExtractor::NetlistExtractor () + : mp_clusters (0), mp_layout (0), mp_cell (0) { // .. nothing yet .. } void -NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl) +NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters) { - const db::Layout &layout = dss.const_layout (); - const db::Cell &cell = dss.const_initial_cell (); + mp_clusters = &clusters; + mp_layout = &dss.const_layout (); + mp_cell = &dss.const_initial_cell (); // gets the text annotation property ID - // this is how the texts are passed for annotating the net names - std::pair text_annot_name_id (false, 0); + m_text_annot_name_id = std::pair (false, 0); if (! dss.text_property_name ().is_nil ()) { - text_annot_name_id = layout.properties_repository ().get_id_of_name (dss.text_property_name ()); + m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (dss.text_property_name ()); } - // gets the device terminal annotation property ID - - // this is how the device extractor conveys terminal shape annotations. - std::pair terminal_annot_name_id; - terminal_annot_name_id = layout.properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_property_name ()); + m_terminal_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_id_property_name ()); + m_device_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_id_property_name ()); + m_device_class_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_class_property_name ()); // the big part: actually extract the nets - m_net_clusters.build (layout, cell, db::ShapeIterator::Polygons, conn); + mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn); // reverse lookup for Circuit vs. cell index std::map circuits; // some circuits may be there because of device extraction - for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { - tl_assert (layout.is_valid_cell_index (c->cell_index ())); + for (db::Netlist::circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + tl_assert (mp_layout->is_valid_cell_index (c->cell_index ())); circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); } std::map > pins_per_cluster_per_cell; - for (db::Layout::bottom_up_const_iterator cid = layout.begin_bottom_up (); cid != layout.end_bottom_up (); ++cid) { + for (db::Layout::bottom_up_const_iterator cid = mp_layout->begin_bottom_up (); cid != mp_layout->end_bottom_up (); ++cid) { - const connected_clusters_type &clusters = m_net_clusters.clusters_per_cell (*cid); + if (cell_is_device_cell (*cid)) { + continue; + } + + const connected_clusters_type &clusters = mp_clusters->clusters_per_cell (*cid); if (clusters.empty ()) { continue; } @@ -79,8 +84,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect std::map::const_iterator k = circuits.find (*cid); if (k == circuits.end ()) { circuit = new db::Circuit (); - nl->add_circuit (circuit); - circuit->set_name (layout.cell_name (*cid)); + nl.add_circuit (circuit); + circuit->set_name (mp_layout->cell_name (*cid)); circuit->set_cell_index (*cid); circuits.insert (std::make_pair (*cid, circuit)); } else { @@ -103,21 +108,13 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } // make subcircuit connections (also make the subcircuits if required) from the connections of the clusters - make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell, layout.dbu ()); + make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell); - // collect the properties - we know that the cluster attributes are property ID's because the - // cluster processor converts shape property IDs to attributes - const local_cluster_type &lc = clusters.cluster_by_id (*c); - for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { - const db::PropertiesRepository::properties_set &ps = layout.properties_repository ().properties (*a); - for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { - if (terminal_annot_name_id.first && j->first == terminal_annot_name_id.second) { - make_device_terminal_from_property (j->second, circuit, net); - } else if (text_annot_name_id.first && j->first == text_annot_name_id.second) { - assign_net_name (j->second.to_string (), net); - } - } - } + // connect devices + connect_devices (circuit, clusters, *c, net); + + // collect labels to net names + collect_labels (clusters, *c, net); if (! clusters.is_root (*c)) { // a non-root cluster makes a pin @@ -130,18 +127,137 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } } +void NetlistExtractor::collect_labels (const connected_clusters_type &clusters, + size_t cid, + db::Net *net) +{ + // collect the properties - we know that the cluster attributes are property ID's because the + // cluster processor converts shape property IDs to attributes + + const local_cluster_type &lc = clusters.cluster_by_id (cid); + for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + + if (m_text_annot_name_id.first && j->first == m_text_annot_name_id.second) { + assign_net_name (j->second.to_string (), net); + } + + } + + } +} + +bool NetlistExtractor::cell_is_device_cell (db::cell_index_type ci) const +{ + if (! m_device_class_annot_name_id.first) { + return false; + } + + const db::Cell &cell = mp_layout->cell (ci); + if (cell.prop_id () == 0) { + return false; + } + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (cell.prop_id ()); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_device_class_annot_name_id.second) { + return true; + } + } + + return false; +} + +bool NetlistExtractor::instance_is_device (db::properties_id_type prop_id) const +{ + if (! prop_id || ! m_device_annot_name_id.first) { + return false; + } + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (prop_id); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_device_annot_name_id.second) { + return true; + } + } + + return false; +} + +db::Device *NetlistExtractor::device_from_instance (db::properties_id_type prop_id, db::Circuit *circuit) const +{ + if (! prop_id || ! m_device_annot_name_id.first) { + return 0; + } + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (prop_id); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_device_annot_name_id.second) { + return circuit->device_by_id (j->second.to ()); + } + } + + return 0; +} + +void NetlistExtractor::connect_devices (db::Circuit *circuit, + const connected_clusters_type &clusters, + size_t cid, + db::Net *net) +{ + const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (cid); + for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + + const db::Instance &inst = i->inst ().inst_ptr; + + // only consider devices in this pass + db::Device *device = device_from_instance (inst.prop_id (), circuit); + if (! device) { + continue; + } + + const db::local_cluster &dc = mp_clusters->clusters_per_cell (inst.cell_index ()).cluster_by_id (i->id ()); + + // connect the net to the terminal of the device: take the terminal ID from the properties on the + // device cluster + for (local_cluster_type::attr_iterator a = dc.begin_attr (); a != dc.end_attr (); ++a) { + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + + if (m_terminal_annot_name_id.first && j->first == m_terminal_annot_name_id.second) { + + size_t tid = j->second.to (); + device->connect_terminal (tid, net); + device->set_cluster_id_for_terminal (tid, i->id ()); + + } + + } + + } + + } +} + void NetlistExtractor::make_and_connect_subcircuits (db::Circuit *circuit, const connected_clusters_type &clusters, size_t cid, db::Net *net, std::map &subcircuits, const std::map &circuits, - const std::map > &pins_per_cluster, - double dbu) + const std::map > &pins_per_cluster) { const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (cid); for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + // skip devices in this pass + if (instance_is_device (i->inst ().inst_ptr.prop_id ())) { + continue; + } + db::SubCircuit *subcircuit = 0; db::cell_index_type ccid = i->inst ().inst_ptr.cell_index (); @@ -154,7 +270,7 @@ void NetlistExtractor::make_and_connect_subcircuits (db::Circuit *circuit, tl_assert (k != circuits.end ()); // because we walk bottom-up subcircuit = new db::SubCircuit (k->second); - db::CplxTrans dbu_trans (dbu); + db::CplxTrans dbu_trans (mp_layout->dbu ()); subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); circuit->add_subcircuit (subcircuit); subcircuits.insert (std::make_pair (i->inst (), subcircuit)); @@ -183,19 +299,6 @@ size_t NetlistExtractor::make_pin (db::Circuit *circuit, db::Net *net) return pin_id; } -void NetlistExtractor::make_device_terminal_from_property (const tl::Variant &v, db::Circuit *circuit, db::Net *net) -{ - if (v.is_user ()) { - const db::NetlistProperty *np = &v.to_user (); - const db::DeviceTerminalProperty *tp = dynamic_cast (np); - if (tp) { - db::Device *device = circuit->device_by_id (tp->device_id ()); - tl_assert (device != 0); - device->connect_terminal (tp->terminal_id (), net); - } - } -} - void NetlistExtractor::assign_net_name (const std::string &n, db::Net *net) { std::string nn = n; diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index 30a48047f..70d6f4565 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -36,6 +36,7 @@ class Netlist; class Circuit; class SubCircuit; class Net; +class Device; /** * @brief The Netlist Extractor @@ -80,22 +81,21 @@ public: * @brief Extract the nets * See the class description for more details. */ - void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl); - - /** - * @brief Gets the shape clusters - * See the class description for more details. - */ - const hier_clusters_type &clusters () const - { - return m_net_clusters; - } + void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters); private: - hier_clusters_type m_net_clusters; + hier_clusters_type *mp_clusters; + const db::Layout *mp_layout; + const db::Cell *mp_cell; + std::pair m_text_annot_name_id; + std::pair m_device_annot_name_id; + std::pair m_terminal_annot_name_id; + std::pair m_device_class_annot_name_id; - void make_device_terminal_from_property (const tl::Variant &v, db::Circuit *circuit, db::Net *net); void assign_net_name (const std::string &n, db::Net *net); + bool instance_is_device (db::properties_id_type prop_id) const; + bool cell_is_device_cell (db::cell_index_type ci) const; + db::Device *device_from_instance (db::properties_id_type prop_id, db::Circuit *circuit) const; /** * @brief Make a pin connection from clusters @@ -120,8 +120,6 @@ private: * - circuits: a lookup table of circuits vs. cell index (reverse lookup) * - pins_per_cluster: a per-cell, reverse lookup table for the pin id per child cell clusters * (used to find the pin ID of a subcircuit from the child cell cluster ID) - * - dbu: the database unit (used to compute the micron-unit transformation of the child - * cell) * Updates: * - subcircuits: An cell instance to SubCircuit lookup table */ @@ -131,8 +129,30 @@ private: db::Net *net, std::map &subcircuits, const std::map &circuits, - const std::map > &pins_per_cluster, - double dbu); + const std::map > &pins_per_cluster); + + /** + * @brief Connects the devices + * + * Devices are identified by special cells. These carry a property with the + * device class name. Inside these cells, the terminals are identified by special clusters. + * The terminal IDs are coded on these clusters are a property. + */ + void connect_devices (db::Circuit *circuit, + const connected_clusters_type &clusters, + size_t cid, + db::Net *net); + + /** + * @brief Attaches net names from text properties + * + * Texts (labels) are represented by special shapes. The texts are kept as properties. + * This method will collect all these labels and attach them to the nets as (alternative) + * names. + */ + void collect_labels (const connected_clusters_type &clusters, + size_t cid, + db::Net *net); }; } diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 36c1d1ffd..28219f41b 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -51,70 +51,6 @@ static unsigned int layer_of (const db::Region ®ion) return dr->deep_layer ().layer (); } -static std::string device_name (const db::Device &device) -{ - if (device.name ().empty ()) { - return "$" + tl::to_string (device.id ()); - } else { - return device.name (); - } -} - -namespace -{ - -class MOSFET3Extractor - : public db::NetlistDeviceExtractorMOS3Transistor -{ -public: - MOSFET3Extractor (const std::string &name, db::Layout *debug_out) - : db::NetlistDeviceExtractorMOS3Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) - { - if (mp_debug_out) { - m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); - m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); - } - } - -private: - db::Layout *mp_debug_out; - unsigned int m_ldiff, m_lgate; - - void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) - { - if (! mp_debug_out) { - return; - } - - std::string cn = layout ()->cell_name (cell_index ()); - std::pair target_cp = mp_debug_out->cell_by_name (cn.c_str ()); - tl_assert (target_cp.first); - - db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->circuit ()->name () + "_" + device_name (*device)).c_str ()); - mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ())); - - db::Cell &device_cell = mp_debug_out->cell (dci); - for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) { - device_cell.shapes (m_ldiff).insert (*p); - } - for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) { - device_cell.shapes (m_lgate).insert (*p); - } - - std::string ps; - const std::vector &pd = device->device_class ()->parameter_definitions (); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (! ps.empty ()) { - ps += ","; - } - ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ())); - } - device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ()))); - } -}; - -} - static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) { unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); @@ -124,6 +60,8 @@ static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_la static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) { + std::set device_cells_seen; + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { db::Cell &cell = ly.cell (cmap.cell_mapping (c->cell_index ())); @@ -154,6 +92,40 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters< } + for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + + if (device_cells_seen.find (d->cell_index ()) != device_cells_seen.end ()) { + continue; + } + + db::Cell &device_cell = ly.cell (cmap.cell_mapping (d->cell_index ())); + device_cells_seen.insert (d->cell_index ()); + + std::string ps; + const std::vector &pd = d->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (! ps.empty ()) { + ps += ","; + } + ps += i->name () + "=" + tl::to_string (d->parameter_value (i->id ())); + } + + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + + const db::local_cluster &dc = clusters.clusters_per_cell (d->cell_index ()).cluster_by_id (d->cluster_id_for_terminal (t->id ())); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + db::Shapes &target = device_cell.shapes (m->second); + for (db::local_cluster::shape_iterator s = dc.begin (m->first); !s.at_end (); ++s) { + target.insert (*s); + } + } + + } + + } + } } @@ -235,24 +207,22 @@ TEST(1_DeviceAndNetExtraction) // perform the extraction db::Netlist nl; + db::hier_clusters cl; - // NOTE: the device extractor will add more debug layers for the transistors: - // 20/0 -> Diffusion - // 21/0 -> Gate - MOSFET3Extractor pmos_ex ("PMOS", &ly); - MOSFET3Extractor nmos_ex ("NMOS", &ly); + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); db::NetlistDeviceExtractor::input_layers dl; dl["SD"] = &rpsd; dl["G"] = &rpgate; dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes - pmos_ex.extract (dss, dl, &nl); + pmos_ex.extract (dss, dl, nl, cl); dl["SD"] = &rnsd; dl["G"] = &rngate; dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes - nmos_ex.extract (dss, dl, &nl); + nmos_ex.extract (dss, dl, nl, cl); // perform the net extraction @@ -282,7 +252,7 @@ TEST(1_DeviceAndNetExtraction) // extract the nets - net_ex.extract_nets (dss, conn, &nl); + net_ex.extract_nets (dss, conn, nl, cl); // debug layers produced for nets // 202/0 -> Active @@ -306,7 +276,7 @@ TEST(1_DeviceAndNetExtraction) // write nets to layout db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); - dump_nets_to_layout (nl, net_ex.clusters (), ly, dump_map, cm); + dump_nets_to_layout (nl, cl, ly, dump_map, cm); // compare netlist as string EXPECT_EQ (nl.to_string (), diff --git a/testdata/algo/device_extract_au1.gds b/testdata/algo/device_extract_au1.gds index a970a662c27bcbfaccc543ce1258a0205de3decc..903b6bf0d9ded99ed198b20e4753b10494c08656 100644 GIT binary patch delta 1440 zcmcIkyGjF55S_hAHgS!?Xnch{EFxGaA}Peeum(|rnuuB?i~c}tY)qv@u(C6Zg(i() zAy|m8AYv5|EG)H&TH0BNKVa5-H@SIWu2^U_!<;>5XXfne)$IF}1~L$m1fN9guONX7 z04BlJn1h4J3$ZAG$6||2?@!F%oqbHsT#ua;v{mrgguI-V>Jv!j?w>>u-biBXoTPBi zQWSDEYYM|QF|Node3dVS{Bt|icNFoqO{|euj!|z{vx3pUDa$jV_+nxuG!9k=fZRdg z5o8ykn;q%27{=$iVU&kh1dv_~!`S1I*I5Ls8(Q(H$_~S=EWus3$H^;W;WiO+GXTG>-M}Hn2HrHaz^^20_1k{XDj?VfJrq(BC?@i16Gp-31Y* z4|@hA5s1m2L)`AY$SMFz7d2Y@Tlxu8`(=S~g?c1++7j`F@s2Hn?GkSL*wOI4vMuNwx}0`ED={7+#CcU z&_&w(T%?0S5y3TB#7QY+aFFUC=;-Dm9dvDdNke*+zE~WN_xt_c@B8??@9*8Oxoy7z z6bQKmd4a@#fC_oYfCfS|2AR;MMGxSNDAnp~vsZer_NohSr(bL~Rv|BObsLS);2gQ} z@f&Xo-wFJ^*CfOp;%@B_fvf*6hS;1?a9^~C>mP1!LJIM`^oaFocu(5m_FbeQ&eMLM zf=}ohTuUhUiBkM(OyhG_3k)0Q z>8F>9tWsZKVWsNK+5oW>`-Vr~!JgqbaTqt%V_fBtxP|Qj7d?^d;9&pZ7_I@#A=d!$ zI2Scf!xbF)Rf=%=X5X*2$@eR<_n&_Gr}8VpRg7@;23-D$T!(&fx&Gx>+i=c~pJR7h zeL%Pm)=O^k_PW257XR-nq@GTUu8W`5oHELq1-zfjB=vOkU6*ktZ7D}{tFZhWen`E< zDJ|apnZ7IGy6NF{vxd8-i-F}~+N$BY<)WVT@C%EBtc$xW?qqAI=WH(b?OaFv3*@8b A#Q*>R From 5962d66940efba4016c6a8a85330c52246f0e8e9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 15 Jan 2019 21:33:41 +0100 Subject: [PATCH 196/335] WIP: major enhancements with respect to device handling The device handling in the netlist extractor was now entirely moved to device cells. New options are introduced for exporting these cells. Tests have been updated. --- src/db/db/db.pro | 2 - src/db/db/dbCellMapping.cc | 4 +- src/db/db/dbCellMapping.h | 6 +- src/db/db/dbDeepShapeStore.cc | 14 +- src/db/db/dbDeepShapeStore.h | 4 +- src/db/db/dbLayoutToNetlist.cc | 119 +++++---- src/db/db/dbLayoutToNetlist.h | 21 +- src/db/db/dbNetlistDeviceExtractor.cc | 33 ++- src/db/db/dbNetlistDeviceExtractor.h | 7 +- src/db/db/dbNetlistExtractor.cc | 25 +- src/db/db/dbNetlistExtractor.h | 2 - src/db/db/dbNetlistProperty.cc | 197 -------------- src/db/db/dbNetlistProperty.h | 241 ------------------ src/db/db/gsiDeclDbLayoutToNetlist.cc | 36 ++- src/db/unit_tests/dbLayoutToNetlistTests.cc | 176 +++---------- src/db/unit_tests/dbNetlistExtractorTests.cc | 1 - src/db/unit_tests/dbNetlistPropertyTests.cc | 63 ----- src/db/unit_tests/unit_tests.pro | 1 - testdata/algo/deep_region_au4b.gds | Bin 9258 -> 8810 bytes .../algo/device_extract_au1_rebuild_ff.gds | Bin 3592 -> 3120 bytes .../algo/device_extract_au1_rebuild_fr.gds | Bin 8206 -> 7734 bytes .../algo/device_extract_au1_rebuild_nf.gds | Bin 4324 -> 3852 bytes .../algo/device_extract_au1_rebuild_nr.gds | Bin 9874 -> 11642 bytes .../algo/device_extract_au1_with_rec_nets.gds | Bin 50894 -> 50630 bytes .../algo/device_extract_au2_with_rec_nets.gds | Bin 40390 -> 38230 bytes .../algo/device_extract_au3_with_rec_nets.gds | Bin 49146 -> 46986 bytes .../algo/device_extract_au4_with_rec_nets.gds | Bin 51962 -> 49546 bytes .../algo/device_extract_au5_with_rec_nets.gds | Bin 73106 -> 70690 bytes 28 files changed, 196 insertions(+), 756 deletions(-) delete mode 100644 src/db/db/dbNetlistProperty.cc delete mode 100644 src/db/db/dbNetlistProperty.h delete mode 100644 src/db/unit_tests/dbNetlistPropertyTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 0da3ca0ca..f45a74d1a 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -142,7 +142,6 @@ SOURCES = \ dbHierProcessor.cc \ dbDeepRegion.cc \ dbHierNetworkProcessor.cc \ - dbNetlistProperty.cc \ dbNetlist.cc \ gsiDeclDbNetlist.cc \ dbNetlistDeviceClasses.cc \ @@ -271,7 +270,6 @@ HEADERS = \ dbHierarchyBuilder.h \ dbLocalOperation.h \ dbHierProcessor.h \ - dbNetlistProperty.h \ dbNetlist.h \ dbNetlistDeviceClasses.h \ dbNetlistDeviceExtractor.h \ diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index f942981bd..9554e79ba 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -283,7 +283,7 @@ CellMapping::create_from_names (const db::Layout &layout_a, db::cell_index_type } std::vector -CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /*cell_index_a*/, const db::Layout &layout_b, db::cell_index_type cell_index_b) +CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /*cell_index_a*/, const db::Layout &layout_b, db::cell_index_type cell_index_b, const std::set *exclude_cells) { std::vector new_cells; std::vector new_cells_b; @@ -293,7 +293,7 @@ CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type / called_b.insert (cell_index_b); for (std::set::const_iterator b = called_b.begin (); b != called_b.end (); ++b) { - if (m_b2a_mapping.find (*b) == m_b2a_mapping.end ()) { + if (m_b2a_mapping.find (*b) == m_b2a_mapping.end () && (! exclude_cells || exclude_cells->find (*b) == exclude_cells->end ())) { db::cell_index_type new_cell = layout_a.add_cell (layout_b.cell_name (*b)); new_cells.push_back (new_cell); new_cells_b.push_back (*b); diff --git a/src/db/db/dbCellMapping.h b/src/db/db/dbCellMapping.h index 854fd1ffa..63740b1ca 100644 --- a/src/db/db/dbCellMapping.h +++ b/src/db/db/dbCellMapping.h @@ -30,6 +30,8 @@ #include "dbTypes.h" #include +#include +#include namespace db { @@ -186,9 +188,11 @@ public: * left unmapped. This method allows creating mappings for these missing cells by adding * new cells and the corresponding instances into the target layout_a. * + * If given, "exclude_cells" can specify a list of cells not to map. + * * The returned vector lists the new cells. */ - std::vector create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b); + std::vector create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b, const std::set *exclude_cells = 0); private: void extract_unique (std::map >::const_iterator cand, diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index b677bcfff..438456bc4 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -388,7 +388,7 @@ DeepShapeStore::invalidate_hier () } const db::CellMapping & -DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell) +DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set *excluded_cells) { const db::Layout *source_layout = &m_layouts [layout_index]->layout; if (source_layout->begin_top_down () == source_layout->end_top_cells ()) { @@ -436,21 +436,21 @@ DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_ } - // Add new cells for the variants and (possible) devices which are cells added during the device - // extraction process - cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top); - } else if (into_layout->cells () == 1) { // Another simple case is mapping into an empty (or single-top-cell-only) layout, where we can use "create_from_single_full". - cm->second.create_single_mapping_full (*into_layout, into_cell, *source_layout, source_top); + cm->second.create_single_mapping (*into_layout, into_cell, *source_layout, source_top); } else { - cm->second.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_top); + cm->second.create_from_geometry (*into_layout, into_cell, *source_layout, source_top); } + // Add new cells for the variants and (possible) devices which are cells added during the device + // extraction process + cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top, excluded_cells); + } return cm->second; diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 0c658eb17..306b8f429 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -239,8 +239,10 @@ public: * If necessary, this method will modify the original layout and add new cells. * "layout_index" is the layout to return to it's original. "into_layout" is the original layout, "into_cell" * the original cell. + * + * "excluded_cells" - if not 0 - will exclude the given cells (and flatten them). */ - const db::CellMapping &cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell); + const db::CellMapping &cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set *excluded_cells = 0); /** * @brief For testing diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 57896e388..773a47c1e 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -211,9 +211,22 @@ unsigned int LayoutToNetlist::layer_of (const db::Region ®ion) const return dr->deep_layer ().layer (); } -db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell) +db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells) { - return m_dss.cell_mapping_to_original (0, &layout, cell.cell_index ()); + unsigned int layout_index = 0; + + std::set device_cells; + + if (! with_device_cells) { + const db::Layout &src_layout = m_dss.layout (layout_index); + for (db::Layout::const_iterator c = src_layout.begin (); c != src_layout.end (); ++c) { + if (db::NetlistDeviceExtractor::is_device_cell (src_layout, c->cell_index ())) { + device_cells.insert (c->cell_index ()); + } + } + } + + return m_dss.cell_mapping_to_original (layout_index, &layout, cell.cell_index (), &device_cells); } db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell) @@ -258,26 +271,18 @@ static void deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const T } template -static void deliver_shapes_of_net_recursive (const db::hier_clusters &clusters, const db::Net &net, unsigned int layer_id, To &to) +static void deliver_shapes_of_net_recursive (const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, To &to) { - const db::Circuit *circuit = net.circuit (); - tl_assert (circuit != 0); - - db::cell_index_type ci = circuit->cell_index (); - - for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, net.cluster_id ()); !rci.at_end (); ++rci) { + // deliver the net shapes + for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) { deliver_shape (*rci, to, rci.trans ()); } } template -static void deliver_shapes_of_net_nonrecursive (const db::hier_clusters &clusters, const db::Net &net, unsigned int layer_id, To &to) +static void deliver_shapes_of_net_nonrecursive (const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, To &to) { - const db::Circuit *circuit = net.circuit (); - tl_assert (circuit != 0); - - db::cell_index_type ci = circuit->cell_index (); - const db::local_cluster &lc = clusters.clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); + const db::local_cluster &lc = clusters.clusters_per_cell (ci).cluster_by_id (cid); for (db::local_cluster::shape_iterator s = lc.begin (layer_id); !s.at_end (); ++s) { deliver_shape (*s, to, db::UnitTrans ()); @@ -287,71 +292,95 @@ static void deliver_shapes_of_net_nonrecursive (const db::hier_clusterscell_index (), net.cluster_id (), lid, to); } else { - deliver_shapes_of_net_recursive (m_net_clusters, net, lid, to); + deliver_shapes_of_net_recursive (m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, to); } } db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const { unsigned int lid = layer_of (of_layer); + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + std::auto_ptr res (new db::Region ()); if (! recursive) { - deliver_shapes_of_net_nonrecursive (m_net_clusters, net, lid, *res); + deliver_shapes_of_net_nonrecursive (m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, *res); } else { - deliver_shapes_of_net_recursive (m_net_clusters, net, lid, *res); + deliver_shapes_of_net_recursive (m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, *res); } return res.release (); } void -LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, std::map, db::cell_index_type> &cmap) const +LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap) const { - for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { - shapes_of_net (net, *l->second, false, target_cell.shapes (l->first)); - } - - if (! cell_name_prefix) { - return; - } - const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); - const db::connected_clusters &clusters = m_net_clusters.clusters_per_cell (circuit->cell_index ()); + build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, cell_name_prefix, device_cell_name_prefix, cmap); +} + +void +LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap) const +{ + for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { + deliver_shapes_of_net_nonrecursive (m_net_clusters, ci, cid, layer_of (*l->second), target_cell.shapes (l->first)); + } + + if (! cell_name_prefix && ! device_cell_name_prefix) { + return; + } + + const db::connected_clusters &clusters = m_net_clusters.clusters_per_cell (ci); typedef db::connected_clusters::connections_type connections_type; - const connections_type &connections = clusters.connections_for_cluster (net.cluster_id ()); + const connections_type &connections = clusters.connections_for_cluster (cid); for (connections_type::const_iterator c = connections.begin (); c != connections.end (); ++c) { - db::cell_index_type ci = c->inst ().inst_ptr.cell_index (); - const db::Circuit *subcircuit = mp_netlist->circuit_by_cell_index (ci); - tl_assert (subcircuit != 0); + db::cell_index_type subci = c->inst ().inst_ptr.cell_index (); + size_t subcid = c->id (); - const db::Net *subnet = subcircuit->net_by_cluster_id (c->id ()); - tl_assert (subnet != 0); - - std::map, db::cell_index_type>::const_iterator cm = cmap.find (std::make_pair (ci, c->id ())); + std::map, db::cell_index_type>::const_iterator cm = cmap.find (std::make_pair (subci, subcid)); if (cm == cmap.end ()) { - db::cell_index_type target_ci = target.add_cell ((std::string (cell_name_prefix) + subcircuit->name ()).c_str ()); - cm = cmap.insert (std::make_pair (std::make_pair (ci, c->id ()), target_ci)).first; + const char *name_prefix = 0; + if (db::NetlistDeviceExtractor::is_device_cell (*internal_layout (), subci)) { + name_prefix = device_cell_name_prefix; + } else { + name_prefix = cell_name_prefix; + } - build_net_rec (*subnet, target, target.cell (target_ci), lmap, cell_name_prefix, cmap); + if (name_prefix) { + + std::string cell_name = internal_layout ()->cell_name (subci); + + db::cell_index_type target_ci = target.add_cell ((std::string (name_prefix) + cell_name).c_str ()); + cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), target_ci)).first; + + build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, cell_name_prefix, device_cell_name_prefix, cmap); + + } else { + cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), std::numeric_limits::max ())).first; + } } - target_cell.insert (db::CellInstArray (db::CellInst (cm->second), c->inst ().complex_trans ())); + if (cm->second != std::numeric_limits::max ()) { + target_cell.insert (db::CellInstArray (db::CellInst (cm->second), c->inst ().complex_trans ())); + } } } void -LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix) const +LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix) const { if (! m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); @@ -359,11 +388,11 @@ LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &ta std::map, db::cell_index_type> cell_map; - build_net_rec (net, target, target_cell, lmap, cell_name_prefix, cell_map); + build_net_rec (net, target, target_cell, lmap, cell_name_prefix, device_cell_name_prefix, cell_map); } void -LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix) const +LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const { if (! m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); @@ -409,7 +438,7 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target } - build_net_rec (*n, target, target.cell (net_ci), lmap, circuit_cell_name_prefix, cell_map); + build_net_rec (*n, target, target.cell (net_ci), lmap, circuit_cell_name_prefix, device_cell_name_prefix, cell_map); } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index af30a3436..d2f5649e8 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -214,9 +214,11 @@ public: /** * @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout. - * CAUTION: may create new cells in "layout". + * If 'with_device_cells' is true, cells will be produced for devices. These are cells not corresponding to circuits, so they are disabled normally. + * Use this option, if you want to access device terminal shapes per device. + * CAUTION: This function may create new cells in "layout". */ - db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell); + db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells = false); /** * @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout. @@ -275,12 +277,16 @@ public: * Recursive mode is picked when a cell name prefix is given. The new cells will be * named like cell_name_prefix + circuit name. * + * If a device cell name prefix is given, cells will be produced for each device model + * using a name like device_cell_name_prefix + device name. + * * @param target The target layout * @param target_cell The target cell * @param lmap Target layer indexes (keys) and net regions (values) * @param cell_name_prefix Chooses recursive mode if non-null + * @param device_cell_name_prefix See above */ - void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix) const; + void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix) const; /** * @brief Builds a full hierarchical representation of the nets @@ -300,13 +306,17 @@ public: * * Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built * to accomodate the subnets (see build_net in recursive mode). * + * If a device cell name prefix is given, cells will be produced for each device model + * using a name like device_cell_name_prefix + device name. + * * @param cmap The mapping of internal layout to target layout for the circuit mapping * @param target The target layout * @param lmap Target layer indexes (keys) and net regions (values) * @param circuit_cell_name_prefix See method description * @param net_cell_name_prefix See method description + * @param device_cell_name_prefix See above */ - void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix) const; + void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; /** * @brief Finds the net by probing a specific location on the given layer @@ -344,7 +354,8 @@ private: bool m_netlist_extracted; size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path); - void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, std::map, db::cell_index_type> &cmap) const; + void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap) const; + void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap) const; }; } diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index f8c44f331..74b38e3b9 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -21,7 +21,6 @@ */ #include "dbNetlistDeviceExtractor.h" -#include "dbNetlistProperty.h" #include "dbRegion.h" #include "dbHierNetworkProcessor.h" #include "dbDeepRegion.h" @@ -224,16 +223,21 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: } } -bool NetlistDeviceExtractor::is_device_cell (db::cell_index_type ci) const +bool NetlistDeviceExtractor::is_device_cell (const db::Layout &layout, db::cell_index_type ci) { - db::properties_id_type pi = mp_layout->cell (ci).prop_id (); + db::properties_id_type pi = layout.cell (ci).prop_id (); if (pi == 0) { return false; } - const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (pi); + std::pair pn = layout.properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_class_property_name ()); + if (! pn.first) { + return false; + } + + const db::PropertiesRepository::properties_set &ps = layout.properties_repository ().properties (pi); for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { - if (j->first == m_device_class_propname_id) { + if (j->first == pn.second) { return true; } } @@ -241,13 +245,20 @@ bool NetlistDeviceExtractor::is_device_cell (db::cell_index_type ci) const return false; } +bool NetlistDeviceExtractor::is_device_cell (db::cell_index_type ci) const +{ + return is_device_cell (*mp_layout, ci); +} + void NetlistDeviceExtractor::push_new_devices () { db::VCplxTrans dbu_inv = db::CplxTrans (mp_layout->dbu ()).inverted (); - for (std::map::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { + for (std::map::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { - db::Vector disp = dbu_inv * d->first->position () - db::Point (); + db::Device *device = mp_circuit->device_by_id (d->first); + + db::Vector disp = dbu_inv * device->position () - db::Point (); DeviceCellKey key; @@ -265,7 +276,7 @@ void NetlistDeviceExtractor::push_new_devices () const std::vector &pd = mp_device_class->parameter_definitions (); for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { - key.parameters.insert (std::make_pair (p->id (), d->first->parameter_value (p->id ()))); + key.parameters.insert (std::make_pair (p->id (), device->parameter_value (p->id ()))); } db::PropertiesRepository::properties_set ps; @@ -311,11 +322,11 @@ void NetlistDeviceExtractor::push_new_devices () } // make the cell index known to the device - d->first->set_cell_index (c->second); + device->set_cell_index (c->second); // Build a property set for the device ID ps.clear (); - ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (d->first->id ()))); + ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (d->first))); db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (c->second), db::Trans (disp)), pi); @@ -383,7 +394,7 @@ void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id unsigned int layer_index = m_layers [geometry_index]; db::PolygonRef pr (polygon, mp_layout->shape_repository ()); - m_new_devices[device][terminal_id][layer_index].push_back (pr); + m_new_devices[device->id ()][terminal_id][layer_index].push_back (pr); } void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Box &box) diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index bf6281919..3626e4876 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -234,6 +234,11 @@ public: */ static const tl::Variant &device_class_property_name (); + /** + * @brief Returns true, if the given cell is a device cell + */ + static bool is_device_cell (const db::Layout &layout, db::cell_index_type ci); + /** * @brief Performs the extraction * @@ -507,7 +512,7 @@ private: layer_definitions m_layer_definitions; std::vector m_layers; error_list m_errors; - std::map m_new_devices; + std::map m_new_devices; std::map m_device_cells; // no copying diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index de0e4c118..2d0b95248 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -23,7 +23,6 @@ #include "dbNetlistExtractor.h" #include "dbDeepShapeStore.h" #include "dbNetlistDeviceExtractor.h" -#include "dbNetlistProperty.h" namespace db { @@ -50,7 +49,6 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect m_terminal_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_id_property_name ()); m_device_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_id_property_name ()); - m_device_class_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_class_property_name ()); // the big part: actually extract the nets @@ -68,7 +66,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect std::map > pins_per_cluster_per_cell; for (db::Layout::bottom_up_const_iterator cid = mp_layout->begin_bottom_up (); cid != mp_layout->end_bottom_up (); ++cid) { - if (cell_is_device_cell (*cid)) { + if (db::NetlistDeviceExtractor::is_device_cell (*mp_layout, *cid)) { continue; } @@ -149,27 +147,6 @@ void NetlistExtractor::collect_labels (const connected_clusters_type &clusters, } } -bool NetlistExtractor::cell_is_device_cell (db::cell_index_type ci) const -{ - if (! m_device_class_annot_name_id.first) { - return false; - } - - const db::Cell &cell = mp_layout->cell (ci); - if (cell.prop_id () == 0) { - return false; - } - - const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (cell.prop_id ()); - for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { - if (j->first == m_device_class_annot_name_id.second) { - return true; - } - } - - return false; -} - bool NetlistExtractor::instance_is_device (db::properties_id_type prop_id) const { if (! prop_id || ! m_device_annot_name_id.first) { diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index 70d6f4565..5447f63b5 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -90,11 +90,9 @@ private: std::pair m_text_annot_name_id; std::pair m_device_annot_name_id; std::pair m_terminal_annot_name_id; - std::pair m_device_class_annot_name_id; void assign_net_name (const std::string &n, db::Net *net); bool instance_is_device (db::properties_id_type prop_id) const; - bool cell_is_device_cell (db::cell_index_type ci) const; db::Device *device_from_instance (db::properties_id_type prop_id, db::Circuit *circuit) const; /** diff --git a/src/db/db/dbNetlistProperty.cc b/src/db/db/dbNetlistProperty.cc deleted file mode 100644 index 1680c86d7..000000000 --- a/src/db/db/dbNetlistProperty.cc +++ /dev/null @@ -1,197 +0,0 @@ - -/* - - 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 "dbNetlistProperty.h" -#include "tlString.h" - -#include - -namespace tl -{ - -void VariantUserClass::destroy (void *p) const -{ - delete (db::NetlistProperty *) p; -} - -bool VariantUserClass::equal (const void *a, const void *b) const -{ - const db::NetlistProperty *pa = (db::NetlistProperty *) a; - const db::NetlistProperty *pb = (db::NetlistProperty *) b; - if (typeid (*pa) == typeid (*pb)) { - return pa->equals (pb); - } else { - return false; - } -} - -bool VariantUserClass::less (const void *a, const void *b) const -{ - const db::NetlistProperty *pa = (db::NetlistProperty *) a; - const db::NetlistProperty *pb = (db::NetlistProperty *) b; - if (typeid (*pa) == typeid (*pb)) { - return pa->less (pb); - } else { - return typeid (*pa).before (typeid (*pb)); - } -} - -void *VariantUserClass::clone (const void *p) const -{ - return ((const db::NetlistProperty *) p)->clone (); -} - -std::string VariantUserClass::to_string (const void *p) const -{ - return ((const db::NetlistProperty *) p)->to_string (); -} - -void VariantUserClass::read (void * /*p*/, tl::Extractor & /*ex*/) const -{ - // .. nothing yet .. - return; -} - -void VariantUserClass::assign (void *self, const void *other) const -{ - db::NetlistProperty *pself = (db::NetlistProperty *) self; - const db::NetlistProperty *pother = (const db::NetlistProperty *) other; - tl_assert (typeid (*pself) == typeid (*pother)); - pself->assign (pother); -} - -void *VariantUserClass::deref_proxy (tl::Object *proxy) const -{ - return proxy; -} - -void VariantUserClass::register_instance (const tl::VariantUserClassBase *inst, bool is_const) -{ - VariantUserClassBase::register_instance (inst, typeid (db::NetlistProperty), is_const); -} - -void VariantUserClass::unregister_instance (const tl::VariantUserClassBase *inst, bool is_const) -{ - VariantUserClassBase::unregister_instance (inst, typeid (db::NetlistProperty), is_const); -} - -} - -namespace db -{ - -// -------------------------------------------------------------------------------------------- -// NetlistProperty Implementation - -NetlistProperty::NetlistProperty () -{ - // .. nothing yet .. -} - -NetlistProperty::NetlistProperty (const NetlistProperty &) -{ - // .. nothing yet .. -} - -NetlistProperty::~NetlistProperty () -{ - // .. nothing yet .. -} - -const tl::VariantUserClass *NetlistProperty::variant_class () -{ - static tl::VariantUserClass s_cls; - return &s_cls; -} - -// -------------------------------------------------------------------------------------------- -// DeviceTerminalProperty Implementation - -DeviceTerminalProperty::DeviceTerminalProperty () - : NetlistProperty (), m_terminal_id (0), m_device_id (0) -{ - // .. nothing yet .. -} - -DeviceTerminalProperty::DeviceTerminalProperty (const DeviceTerminalProperty &other) - : NetlistProperty (other), m_terminal_id (other.m_terminal_id), m_device_id (other.m_device_id) -{ - // .. nothing yet .. -} - -DeviceTerminalProperty::DeviceTerminalProperty (size_t device_id, size_t terminal_id) - : NetlistProperty (), m_terminal_id (terminal_id), m_device_id (device_id) -{ - // .. nothing yet .. -} - -void DeviceTerminalProperty::set_terminal_ref (size_t device_id, size_t terminal_id) -{ - m_device_id = device_id; - m_terminal_id = terminal_id; -} - -DeviceTerminalProperty &DeviceTerminalProperty::operator= (const DeviceTerminalProperty &other) -{ - NetlistProperty::operator= (other); - if (this != &other) { - m_terminal_id = other.m_terminal_id; - m_device_id = other.m_device_id; - } - return *this; -} - -bool DeviceTerminalProperty::equals (const NetlistProperty *p) const -{ - const DeviceTerminalProperty *pp = static_cast (p); - return NetlistProperty::equals (p) && m_terminal_id == pp->m_terminal_id && m_device_id == pp->m_device_id; -} - -bool DeviceTerminalProperty::less (const NetlistProperty *p) const -{ - if (! NetlistProperty::equals (p)) { - return NetlistProperty::less (p); - } else { - const DeviceTerminalProperty *pp = static_cast (p); - if (m_terminal_id != pp->m_terminal_id) { - return m_terminal_id < pp->m_terminal_id; - } else { - return m_device_id < pp->m_device_id; - } - } -} - -void DeviceTerminalProperty::assign (const NetlistProperty *p) -{ - NetlistProperty::assign (p); - const DeviceTerminalProperty *pp = static_cast (p); - m_terminal_id = pp->m_terminal_id; - m_device_id = pp->m_device_id; -} - - -std::string DeviceTerminalProperty::to_string () const -{ - return tl::to_string (m_device_id) + ":" + tl::to_string (m_terminal_id); -} - -} diff --git a/src/db/db/dbNetlistProperty.h b/src/db/db/dbNetlistProperty.h deleted file mode 100644 index 1eff42c23..000000000 --- a/src/db/db/dbNetlistProperty.h +++ /dev/null @@ -1,241 +0,0 @@ - -/* - - 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_dbNetlistProperty -#define _HDR_dbNetlistProperty - -#include "dbCommon.h" -#include "dbNetlist.h" -#include "tlVariant.h" - -#include - -namespace db -{ - class NetlistProperty; -} - -namespace tl -{ - class Extractor; - - // specialization of tl::VariantUserClass for the purpose of NetlistProperty representation - template <> class DB_PUBLIC VariantUserClass - : public tl::VariantUserClassBase - { - public: - // creation not supported - virtual void *create () const { tl_assert (false); } - - virtual void destroy (void *p) const; - virtual bool equal (const void *a, const void *b) const; - virtual bool less (const void *a, const void *b) const; - virtual void *clone (const void *p) const; - virtual const char *name () const { return ""; } - virtual bool is_const () const { return false; } - virtual const gsi::ClassBase *gsi_cls () const { return 0; } - virtual const tl::EvalClass *eval_cls () const { return 0; } - virtual std::string to_string (const void *p) const; - virtual void read (void *p, tl::Extractor &ex) const; - virtual void assign (void *self, const void *other) const; - virtual void *deref_proxy (tl::Object *proxy) const; - - db::NetlistProperty *get (void *ptr) const { return reinterpret_cast (ptr); } - const db::NetlistProperty *get (const void *ptr) const { return reinterpret_cast (ptr); } - - protected: - void register_instance (const tl::VariantUserClassBase *inst, bool is_const); - void unregister_instance (const tl::VariantUserClassBase *inst, bool is_const); - }; - -} - -namespace db -{ - -/** - * @brief The base class for a netlist property attached to a shape - * - * This class provides a wrapper for binding a netlist property - * to a tl::Variant. Hence it can be kept as a shape property - * in the context of db::Layout's propery repo. - */ -class DB_PUBLIC NetlistProperty -{ -public: - /** - * @brief Gets the class descriptor for keeping the object inside a tl::Variant - * - * For a Variant that owns a NetlistProperty object, use - * - * @code - * db::NetlistProperty *prop = new db::NetlistProperty (); - * bool shared = true; // the variant will own the object - * tl::Variant prop_in_var (prop, prop->variant_class (), shared); - * @endcode - */ - static const tl::VariantUserClass *variant_class (); - - /** - * @brief Constructor - */ - NetlistProperty (); - - /** - * @brief Copy constructor - */ - NetlistProperty (const NetlistProperty &other); - - /** - * @brief (virtual) Destructor - */ - virtual ~NetlistProperty (); - - /** - * @brief Clones the object - */ - virtual NetlistProperty *clone () const - { - return new NetlistProperty (*this); - } - - /** - * @brief Compares two objects (equal). Both types are guaranteed to be the same. - */ - virtual bool equals (const NetlistProperty *) const - { - return true; - } - - /** - * @brief Compares two objects (less). Both types are guaranteed to be the same. - */ - virtual bool less (const NetlistProperty *) const - { - return false; - } - - /** - * @brief Assigned the other object to self. Both types are guaranteed to be identical. - */ - virtual void assign (const NetlistProperty *) - { - // .. nothing yet .. - } - - /** - * @brief Converts to a string - */ - virtual std::string to_string () const - { - return std::string (); - } -}; - -/** - * @brief A reference to a device terminal - * - * This property is used to mark a shape as a device terminal reference. - * Such a terminal reference points to a terminal of a specific device. - * Attaching such a property to a shape allows connecting the - * net to the device later. - */ -class DB_PUBLIC DeviceTerminalProperty - : public db::NetlistProperty -{ -public: - /** - * @brief Creates a netlist name property without a specific name - */ - DeviceTerminalProperty (); - - /** - * @brief copy constructor - */ - DeviceTerminalProperty (const db::DeviceTerminalProperty &other); - - /** - * @brief Creates a netlist name property with the given name - */ - DeviceTerminalProperty (size_t device_id, size_t terminal_id); - - /** - * @brief Assignment - */ - DeviceTerminalProperty &operator= (const DeviceTerminalProperty &other); - - /** - * @brief Sets the terminal reference - */ - void set_terminal_ref (size_t device_id, size_t terminal_id); - - /** - * @brief Gets the terminal ID - */ - size_t terminal_id () const - { - return m_terminal_id; - } - - /** - * @brief Gets the device ID - */ - size_t device_id () const - { - return m_device_id; - } - - /** - * @brief Clones the object - */ - virtual DeviceTerminalProperty *clone () const - { - return new DeviceTerminalProperty (*this); - } - - /** - * @brief Compares two objects (equal). Both types are guaranteed to be the same. - */ - virtual bool equals (const NetlistProperty *) const; - - /** - * @brief Compares two objects (less). Both types are guaranteed to be the same. - */ - virtual bool less (const NetlistProperty *) const; - - /** - * @brief Assigned the other object to self. Both types are guaranteed to be identical. - */ - virtual void assign (const NetlistProperty *); - - /** - * @brief Converts to a string - */ - virtual std::string to_string () const; - -private: - size_t m_terminal_id, m_device_id; -}; - -} - -#endif diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index ad0539767..884ad8f94 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -43,17 +43,19 @@ static db::Cell *l2n_internal_top_cell (db::LayoutToNetlist *l2n) return const_cast (l2n->internal_top_cell ()); } -static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &cell_name_prefix) +static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) { - std::string p = cell_name_prefix.to_string (); - l2n->build_net (net, target, target_cell, lmap, cell_name_prefix.is_nil () ? 0 : p.c_str ()); + std::string p = circuit_cell_name_prefix.to_string (); + std::string dp = device_cell_name_prefix.to_string (); + l2n->build_net (net, target, target_cell, lmap, circuit_cell_name_prefix.is_nil () ? 0 : p.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } -static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &circuit_cell_name_prefix) +static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) { std::string cp = circuit_cell_name_prefix.to_string (); std::string np = net_cell_name_prefix.to_string (); - l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str ()); + std::string dp = device_cell_name_prefix.to_string (); + l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", @@ -150,9 +152,11 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "This method is required to derive the internal layer index - for example for\n" "investigating the cluster tree.\n" ) + - gsi::method ("cell_mapping_into", &db::LayoutToNetlist::cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"), + gsi::method ("cell_mapping_into", &db::LayoutToNetlist::cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("with_device_cells", false), "@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n" - "CAUTION: may create new cells in 'layout'.\n" + "If 'with_device_cells' is true, cells will be produced for devices. These are cells not corresponding to circuits, so they are disabled normally.\n" + "Use this option, if you want to access device terminal shapes per device.\n" + "CAUTION: this function may create new cells in 'layout'.\n" ) + gsi::method ("const_cell_mapping_into", &db::LayoutToNetlist::const_cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"), "@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n" @@ -172,7 +176,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "If 'recursive'' is true, the returned region will contain the shapes of\n" "all subcircuits too.\n" ) + - gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("cell_name"), + gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), "@brief Builds a net representation in the given layout and cell\n" "\n" "This method has two modes: recursive and top-level mode. In recursive mode,\n" @@ -182,15 +186,19 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "In top-level mode, only the shapes from the net inside it's circuit are copied to\n" "the given target cell. No other cells are created.\n" "\n" - "Recursive mode is picked when a cell name prefix is given. The new cells will be\n" - "named like cell_name_prefix + circuit name.\n" + "Recursive mode is picked when a circuit cell name prefix is given. The new cells will be\n" + "named like circuit_cell_name_prefix + circuit name.\n" + "\n" + "If a device cell name prefix is given, device shapes will be output on device cells named\n" + "like device_cell_name_prefix + device name.\n" "\n" "@param target The target layout\n" "@param target_cell The target cell\n" "@param lmap Target layer indexes (keys) and net regions (values)\n" - "@param cell_name_prefix Chooses recursive mode if non-nil\n" + "@param circuit_cell_name_prefix Chooses recursive mode if non-nil\n" + "@param device_cell_name_prefix If given, devices will be output as separate cells\n" ) + - gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix"), gsi::arg ("circuit_cell_name_prefix"), + gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), "@brief Builds a full hierarchical representation of the nets\n" "\n" "This method copies all nets into cells corresponding to the circuits. It uses the cmap\n" @@ -214,11 +222,15 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", " to accomodate the subnets (see build_net in recursive mode). @/li\n" "@/ul\n" "\n" + "If a device name prefix is given, device shapes will be output on device cells named\n" + "like device_name_prefix + device name.\n" + "\n" "@param cmap The mapping of internal layout to target layout for the circuit mapping\n" "@param target The target layout\n" "@param lmap Target layer indexes (keys) and net regions (values)\n" "@param circuit_cell_name_prefix See method description\n" "@param net_cell_name_prefix See method description\n" + "@param device_cell_name_prefix If given, devices will be output as separate cells\n" ) + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), "@brief Finds the net by probing a specific location on the given layer\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 250b91ac0..cc358fee1 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -37,103 +37,11 @@ #include #include -namespace -{ - static std::string qnet_name (const db::Net *net) { return net ? net->qname () : "(null)"; } -static std::string device_name (const db::Device &device) -{ - if (device.name ().empty ()) { - return "$" + tl::to_string (device.id ()); - } else { - return device.name (); - } -} - -static void mos2layout (const db::Layout *layout, db::cell_index_type cell_index, db::Layout *debug_out, const db::Device *device, unsigned int ldiff, const db::Region &diff, unsigned int lgate, const db::Region &gate) -{ - std::string cn = layout->cell_name (cell_index); - std::pair target_cp = debug_out->cell_by_name (cn.c_str ()); - tl_assert (target_cp.first); - - db::cell_index_type dci = debug_out->add_cell ((device->device_class ()->name () + "_" + device->circuit ()->name () + "_" + device_name (*device)).c_str ()); - debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ())); - - db::Cell &device_cell = debug_out->cell (dci); - for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) { - device_cell.shapes (ldiff).insert (*p); - } - for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) { - device_cell.shapes (lgate).insert (*p); - } - - std::string ps; - const std::vector &pd = device->device_class ()->parameter_definitions (); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (! ps.empty ()) { - ps += ","; - } - ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ())); - } - device_cell.shapes (ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ()))); -} - -class MOSFET3Extractor - : public db::NetlistDeviceExtractorMOS3Transistor -{ -public: - MOSFET3Extractor (const std::string &name, db::Layout *debug_out) - : db::NetlistDeviceExtractorMOS3Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) - { - if (mp_debug_out) { - m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); - m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); - } - } - -private: - db::Layout *mp_debug_out; - unsigned int m_ldiff, m_lgate; - - void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) - { - if (mp_debug_out) { - mos2layout (layout (), cell_index (), mp_debug_out, device, m_ldiff, diff, m_lgate, gate); - } - } -}; - -class MOSFET4Extractor - : public db::NetlistDeviceExtractorMOS4Transistor -{ -public: - MOSFET4Extractor (const std::string &name, db::Layout *debug_out) - : db::NetlistDeviceExtractorMOS4Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) - { - if (mp_debug_out) { - m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); - m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); - } - } - -private: - db::Layout *mp_debug_out; - unsigned int m_ldiff, m_lgate; - - void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) - { - if (mp_debug_out) { - mos2layout (layout (), cell_index (), mp_debug_out, device, m_ldiff, diff, m_lgate, gate); - } - } -}; - -} - static void dump_nets_to_layout (const db::LayoutToNetlist &l2n, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) { const db::Netlist &nl = *l2n.netlist (); @@ -271,25 +179,8 @@ TEST(1_Basic) db::Region rngate = rnactive & *rpoly; db::Region rnsd = rnactive - rngate; - // return the computed layers into the original layout and write it for debugging purposes - - unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate - unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain - unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion - unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion - - rpgate.insert_into (&ly, tc.cell_index (), lgate); - rngate.insert_into (&ly, tc.cell_index (), lgate); - rpsd.insert_into (&ly, tc.cell_index (), lsd); - rnsd.insert_into (&ly, tc.cell_index (), lsd); - rpsd.insert_into (&ly, tc.cell_index (), lpdiff); - rnsd.insert_into (&ly, tc.cell_index (), lndiff); - - // NOTE: the device extractor will add more debug layers for the transistors: - // 20/0 -> Diffusion - // 21/0 -> Gate - MOSFET3Extractor pmos_ex ("PMOS", &ly); - MOSFET3Extractor nmos_ex ("NMOS", &ly); + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); // device extraction @@ -305,6 +196,23 @@ TEST(1_Basic) dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes l2n.extract_devices (nmos_ex, dl); + // return the computed layers into the original layout and write it for debugging purposes + // NOTE: this will include the device layers too + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + unsigned int lpoly = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Poly with gate terminal + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpoly->insert_into (&ly, tc.cell_index (), lpoly); + // net extraction // Intra-layer @@ -356,7 +264,7 @@ TEST(1_Basic) dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); // write nets to layout - db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + db::CellMapping cm = l2n.cell_mapping_into (ly, tc, true /*with device cells*/); dump_nets_to_layout (l2n, ly, dump_map, cm); dump_map.clear (); @@ -425,7 +333,7 @@ TEST(1_Basic) ly2.dbu (ly.dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - db::CellMapping cm = l2n.cell_mapping_into (ly2, top2); + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); std::map lmap; lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; @@ -437,7 +345,7 @@ TEST(1_Basic) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, 0, 0); + l2n.build_all_nets (cm, ly2, lmap, 0, 0, 0); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); @@ -452,7 +360,7 @@ TEST(1_Basic) ly2.dbu (ly.dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - db::CellMapping cm = l2n.cell_mapping_into (ly2, top2); + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); std::map lmap; lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; @@ -464,7 +372,7 @@ TEST(1_Basic) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", 0); + l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, 0); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); @@ -479,7 +387,7 @@ TEST(1_Basic) ly2.dbu (ly.dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - db::CellMapping cm = l2n.cell_mapping_into (ly2, top2); + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); std::map lmap; lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; @@ -491,7 +399,7 @@ TEST(1_Basic) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, 0, "CIRCUIT_"); + l2n.build_all_nets (cm, ly2, lmap, 0, "CIRCUIT_", 0); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); @@ -506,7 +414,7 @@ TEST(1_Basic) ly2.dbu (ly.dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - db::CellMapping cm = l2n.cell_mapping_into (ly2, top2); + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); std::map lmap; lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; @@ -518,7 +426,7 @@ TEST(1_Basic) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_"); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); @@ -641,11 +549,8 @@ TEST(2_Probing) rpsd.insert_into (&ly, tc.cell_index (), lpdiff); rnsd.insert_into (&ly, tc.cell_index (), lndiff); - // NOTE: the device extractor will add more debug layers for the transistors: - // 20/0 -> Diffusion - // 21/0 -> Gate - MOSFET3Extractor pmos_ex ("PMOS", &ly); - MOSFET3Extractor nmos_ex ("NMOS", &ly); + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); // device extraction @@ -895,11 +800,8 @@ TEST(3_GlobalNetConnections) rpsd.insert_into (&ly, tc.cell_index (), lptie); rnsd.insert_into (&ly, tc.cell_index (), lntie); - // NOTE: the device extractor will add more debug layers for the transistors: - // 20/0 -> Diffusion - // 21/0 -> Gate - MOSFET3Extractor pmos_ex ("PMOS", &ly); - MOSFET3Extractor nmos_ex ("NMOS", &ly); + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); // device extraction @@ -1166,11 +1068,8 @@ TEST(4_GlobalNetDeviceExtraction) rpsd.insert_into (&ly, tc.cell_index (), lptie); rnsd.insert_into (&ly, tc.cell_index (), lntie); - // NOTE: the device extractor will add more debug layers for the transistors: - // 20/0 -> Diffusion - // 21/0 -> Gate - MOSFET4Extractor pmos_ex ("PMOS", &ly); - MOSFET4Extractor nmos_ex ("NMOS", &ly); + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); // device extraction @@ -1442,11 +1341,8 @@ TEST(5_DeviceExtractionWithDeviceCombination) rpsd.insert_into (&ly, tc.cell_index (), lptie); rnsd.insert_into (&ly, tc.cell_index (), lntie); - // NOTE: the device extractor will add more debug layers for the transistors: - // 20/0 -> Diffusion - // 21/0 -> Gate - MOSFET4Extractor pmos_ex ("PMOS", &ly); - MOSFET4Extractor nmos_ex ("NMOS", &ly); + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); // device extraction diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 28219f41b..44d09a4a9 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -34,7 +34,6 @@ #include "dbWriter.h" #include "dbCommonReader.h" #include "dbTestSupport.h" -#include "dbNetlistProperty.h" #include "dbCellMapping.h" #include "tlUnitTest.h" diff --git a/src/db/unit_tests/dbNetlistPropertyTests.cc b/src/db/unit_tests/dbNetlistPropertyTests.cc deleted file mode 100644 index bbac12a0c..000000000 --- a/src/db/unit_tests/dbNetlistPropertyTests.cc +++ /dev/null @@ -1,63 +0,0 @@ - -/* - - 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 "dbNetlistProperty.h" - -#include "tlUnitTest.h" -#include "tlVariant.h" - -#include - -TEST(1_TerminalRefBasic) -{ - db::DeviceTerminalProperty dp (42, 17); - EXPECT_EQ (dp.to_string (), "42:17"); - EXPECT_EQ (dp.device_id () == 42, true); - EXPECT_EQ (dp.terminal_id () == 17, true); - - dp.set_terminal_ref (2, 1); - EXPECT_EQ (dp.to_string (), "2:1"); - EXPECT_EQ (dp.device_id () == 2, true); - EXPECT_EQ (dp.terminal_id () == 1, true); - - db::DeviceTerminalProperty dp2 = dp; - EXPECT_EQ (dp2.to_string (), "2:1"); -} - -TEST(2_Variants) -{ - std::auto_ptr dp (new db::DeviceTerminalProperty ()); - dp->set_terminal_ref (42, 17); - - tl::Variant v (dp.release (), db::NetlistProperty::variant_class (), true); - EXPECT_EQ (v.is_user (), true); - EXPECT_EQ (dynamic_cast(v.to_user ()).to_string (), "42:17"); - EXPECT_EQ (v.to_string (), "42:17"); - - tl::Variant vv = v; - v = tl::Variant (); - EXPECT_EQ (v.is_user (), false); - EXPECT_EQ (vv.is_user (), true); - EXPECT_EQ (dynamic_cast(vv.to_user ()).to_string (), "42:17"); -} - diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index d1b455c68..c0701254d 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -59,7 +59,6 @@ SOURCES = \ dbDeepRegionTests.cc \ dbDeepShapeStoreTests.cc \ dbHierNetworkProcessorTests.cc \ - dbNetlistPropertyTests.cc \ dbNetlistTests.cc \ dbNetlistExtractorTests.cc \ dbNetlistDeviceExtractorTests.cc \ diff --git a/testdata/algo/deep_region_au4b.gds b/testdata/algo/deep_region_au4b.gds index 8947f5e0210867cb03595ccf892d1296083ad5f9..af9145709eace15ceeb4b26462c2320d4ea1c5d2 100644 GIT binary patch delta 353 zcmZ4G@yaENfsKKQDS|{L4=vr&au{L4=vr&au7M8h; zI3;(mIx$cF#V#?~hF^d3T4vtK75v6PntSp#ep4|0jbD#|hc-)a+E8pp5zh}%0Q3b+ ArvLx| diff --git a/testdata/algo/device_extract_au1_rebuild_ff.gds b/testdata/algo/device_extract_au1_rebuild_ff.gds index afbbb50923d70b2e717885039342897f2cef4e28..c6ec960bd6dbdadd47ef22a2f942500ed66efa97 100644 GIT binary patch delta 441 zcmeB>*&w0Bz{bGD6u}_F$i)7bfsug^h}jslk=YZKRO_(_Gw`snxv2R0`Uk5RGO(~S zFvzgtG=hN(rk0>eu&DuPraEAE8N^tSVnW7ljJnP!jKvd^U6}gnxfmF@gn)hl8^j~P z{Qv*I0-(?b1{OgE1{R`|36g_&A`BT8_0eOqhaEc z3s@D@85lT4A&PiTLlnVim^h~>1B>8fen!#BJZzWQAt%8n{d=aY{HZM7_o~xF~!$o zF@}MMjm<^H&(}X##gKu8oq<7y6{kxWxL|4tssx)FfM#kIb`L^~1t}(IEYJZVL5_aG z41`opzQ^=@vMb9GtU)Bh3X`A_(*{Of@LBK(hb;|0@8d7oZGO7OWc@mvG%Mc>;0E0SsDy#(>F%0>Ybp1P<{7 E0M(XS$p8QV delta 549 zcmdmH)90YXz{bGD6u}_F$i)7bff2-G;AP-OW=~X76~-nU^$?rzWIsmi;!aHQSk)&m zi(?h1WLs0IYD$r74ki`rPOuR4}rt0z-iS@u@L-aWOD(2?2cswwgzP`Tzfa1wf$> z3@m~S3@kud9+)gJY=H6%42%;{knE^(|uzxf%r8lx&Z1A`1J&cwmM!NwNiA26AbPk8eIULQsP D4FQC< diff --git a/testdata/algo/device_extract_au1_rebuild_nr.gds b/testdata/algo/device_extract_au1_rebuild_nr.gds index 19301649d9a82d85a444a52f72759d322613b88e..0050d5e7d6016f3aa29e90dad05e1bca5be11742 100644 GIT binary patch literal 11642 zcmcgy&ud&&6uvW=c{53;noiR$Y7r?@P^h(&nWinp(l&`SRMXPLpdpg}0SbarM5+XA zK`VlwtLmaC)J{eOQ+^q?bf(4mAWy(<}$nlkCowV&*)GXK6ot**LD*c zQ~Es&!w&>yozPUaK)+^ky^f{5b~1g!^Lvp^Ao((;{I#I0JNwi-o!zk0O^;xwkyo`- zetG`PvDrDz-b(UJv8!F! z(M`tiDsT-9j#1Wu5qo3yr}Po`BI)CIg7g!Vb)eP{w10CO{x87BZRni`lcxk_JwmVS zQ5idw_@oSlO=K)*DxEugB8xbyMBaDCMm&B5KAWmakk3pGqaK~(T|PKRyOZ6@?nLgi zOYM<3oOo3H6^n!ed)&ZZl{1Qe*kKvJHyOjLz(26V7-hXTe@Tz{%UGHqeZ(H4toPmuRFmH#R;w)thLtZCz zI$7m$W_Dq`)isq7^aM>i4yuvCjuJdIo~Rxh1=3GrXZ2|G2mMX={o0#oGgjGemK87+ zR#tMYfFqx#t=Rnr?!*3L+1||RS_)QYQ58;1r7!o$VH>Ubdg0eom zm-P#Jv^1AGgK`PGiEg*SxMW*d=aTeRg&bgBD=V5yT#cOagSjMjVlJrOia3N{+`hym zx^;<5jIvH>Jl*-zS&G^SHDE%J!~H*n?eB^z%#l&O#n+u+yVTx{LTWlWvN7o8BB)amX||vlIuU zGwi>5WOBeI62&7_pmh#NU+fr(ZN=(3TQ2p7pR76+>}Nd|Iu+;aQZ_bJRQE{Kesm;s zD$bFJjg8eb4FCCyswrdR7UzectoN>_q({|~_353H@{3W{39YCC`lYX8BM8h z*aaOE{LfC$FSllpGxQFp>Pz%yH(vbKgg1}VU**cb9j}+F!=L zonn4+wf~qq1H`QT`NsUr(f(sb0q{O&?aw#n4mxrCMEr8)23~5tW7htBV}AZl|1o3S z>m9T9=Nt2hef`ILV1SslKi`+-v($KLdv;p~mydOW=8YVYig zCw?B@be(qg#7}yL+6dJ4V?|XHZ;e5ZD{8D+R%LTwbB1AyD*2h{wz*+{s&Gw*(l(&lZ^sS zJJ~4Uw3CejPCMBs;IxyC0!};GDB!e{jRHdIE%n`zG0SgZ zO!hHj?V>Sf-@x28Mp*~y`HuST2YOFK|0rV(UGdrL(2r5pfi7=;n7Pm-?UqKM33^_P^qdy)y|19GJ3g;^5UkJFN18!AuUh|)0Gy<}uVhHo z^f!^>ya- u%$H+^k$Zla%jC*2Ys!^R6v#0X%+<%7GgpqHnJdRUI#=G#kR$sm74si*y2JvrR*QrO*a^L_a?fp zSGBzey$mHX5d`OX3Z5qlB6w2-FCy6bk{H?Hmmb3J<@3JpeZRl=-tV=Ys}&|OQM}Gf zK}>yN0{;`(G)o*E->onu_^wvb($ajPzqK-{iS9?C2iPrQp3T@oYSg?q_05)S~KV^7+4PB`!?{`9iUJ5Q?KJ z7eXH7D`D~2%X#9aSw@mpmg)=3j3rZynFFy;g{hLXaA{?|dSwdFjPs}s+jw1caoZ@8 z%|9{PBws7iJX>m8RKWkX(}C?%OU0wA9#~s8ZM}7zwiqwd{8PDY_qConhe@Y^QzaL> zPS|#BSjD)lkS)%c4U#`M8~uGwPh#IZ8+)YpPSE>yWB7(e>kni2yiM!(>_)$Bp19Ic_>g5*ciQQ^?$P7gR!rPzm8Y?K>z>% diff --git a/testdata/algo/device_extract_au1_with_rec_nets.gds b/testdata/algo/device_extract_au1_with_rec_nets.gds index c61fa0fb5d4a1254e2dc0b4a383984492ee51f7f..1bc3662a113181bf85ed1fd465beb5481ad444c5 100644 GIT binary patch delta 2918 zcmc)MQEXFH7y#gNZ@atNb#;SvVwtTebuxugdNVthdbztY@e*BGmDlqoGA z#ghx)6<=)qVaMrH`hHed#&wf=`U%dElUIJ{h2TelAIpeo7;_Z3R(GaBF5}1DQW|ba z(_Eh^$MIbS_~kMF7$@CdLB2wLm6ICtNK$J!^-AZy@J{A*F(y@ttb$2ikukQd%3&D) z^%};~GiJ}2@P}a-W2L@y9p>f&Zg#}{is%R&t*&ue9}*V>_3LxfeRk1*-w31T2-c9! zYPP&G@9LEqW3&QVk@bj6o5yL->vrCnH_NvMF~YJbfd`HqWNyTqU zy)~)1512lTRC_nAHHP^kOnP+vVODaudv_k@9(1lM3o3=Khg}M|YDyB0&ig~_mjfi` z@2{puPz&ZZJZLS{KiEw)^Ec~Z?V7@@A9XQQtSW~5-&Kc1SE=GA-`mA|2<1mMo)zG0 zp8`+&G`Q*80l8!)^!a4yl`Hds+AShi_H07=pEt8jOcYI>RI{g>B?gJ<8`A6gab0gW(2}e{Cf>)nFrU zJde^7QD9=b1|1QSm$#D~kJ!kcqbP636nHD9K}05*iOKn1Im+MGZyQnl8do3{*PyD2 z)W>m}ucOIkzGKZ<0cH~l#1b0(lOTCGK{EQ1jU3*EbAHjH!k<4y;e^i%zqQC<#4ACc zi@|HMC%-{!Q}*w?bCjeSNlj_?YEJ&2T*DQDu~!V7$8}Aon&FaM!MfX`NEJ2{|>4!GDLEA zkmT%;ja+(jbAbD=k51SG51cvf;7aG&i~y0W3b)Uxa44IA4ECAqF__LN5Xos!%$X&N zFYa8U_4L^`{VsPjxR5`^ooR6PNr4xHFVBAta~HCsZpPdj@bF~4Dm>;BQ?ibpGq}LNlFv9K`Q!!vB;I6LJG_Q|6-F3(_AwxO#~Yq){At5NJLnh~le&7UG@w&7^jFOeHq z%r+xujwyn5Amy^0YYe^e*eN*JDNCF0|`-pu}+TT9^n)#%8@XPCUXiz4mg za(tHxr;p2VpN^dOGg$~wL=-MZ0xJFva5X3pio>kd3@aE z&w99Y^{}mcm7<%~n!MSgBfWi0p6XW+Jy?d)ev0{n6iHTV^2dH1={&^bvB8x&#|Bkg zJ4~UnQiC&tI?ysyStb1Fn?BO%9KLYUD3s+B69&9D*@U|n<0xEY9|db)nf#EQloa=} z+S^);hAivL@{r+6?+8c3wQtS}XL>hRAwK!P%#S~@dVt`k(#qo1s<#?{PCLuWH?KXC kgP6KpQT*t{sb}?8#4djYe_2ufjc;EzZ}ji_>+~P;KWNnt^8f$< diff --git a/testdata/algo/device_extract_au2_with_rec_nets.gds b/testdata/algo/device_extract_au2_with_rec_nets.gds index ddd8db94cf6c971b9b32e2f9cee6394876bdeae6..228fa0ef10d816e2a87ffb3928a25c7c67f9cf82 100644 GIT binary patch delta 1122 zcmZ|NK}Zx)7zglqqmAq8l%%uBmKsZ~?jp13m^+j^yoPNzbm-uzU5a84(WU5Q6a-P= zrPAH`N46c5Iz%i>2oq#=30gWxh$nBIgpd%A9U`Gu?-YD<_?Y4Q^ZVYLH}A_wb*rH! z6irdp3(8TZc5Ew5Iiwg$pYq>(EpeikYL_pO?e%X|8y^_mZ>S}_U(!)+dq=u*yUh6k zI*)24Y-?{x+vq6aLF5xYcecp!Z3lZ<7pt}uqqbaU*A7`Hd0MF19LHf%er^mawvNpysWx-;F$HlhRKexY zM9sTWwa!S@azoU?vqGIJa{MZA+$l=6R*-6~7^2i^p-hisZ-(QBCslJss-_pBex4s1 zB!A+_^^lOxV141?mD$E(T$)9|pN)SmBD`kxgJ;fz=j*aNbKQQKp}U zW_=NS$j*$CnX*38NXY=1IIi>bneN^boju;(8WCyr2yr}%;$STgF>%})6Y@s%~B_{v(`>uck$e0BZK%bV-Vp2^F)PhN)2 z|IEw!uf9AvzNWIiqEuf>UaG$SlvmW|uh_qjvLXK#YoQ%H3kMV92j#~F(+mvp7`Ouh z@J=2iJ_4(}0HZEmmJS?YY3Ex`n4U23$*d69%n@iXW0)i33{_v;N~NH$WYU<^JP&kD zloXoGT#n}bspWhW%@j?{XmH(PlnX7!e5yxxPtPL-a0W{bScz~Qv$ooBaga7Y8LW(BO#Ur47f=l z5`qNVg0fGdNoAi!FQKY^9{B9$VOEsjvWVfGs8E-D^xWlCE1@iH3N;y!peKMK9Z;yV zZ7S6Lc0w6K3Y82>a59KtEvQh(Ln_q04nl2*Juoj7!tF4IAh}+Uapq*SM^vbxW0e)u zCN9NQ9()|QtDzEihEfcCO}0Z{G6F4042z`ZN=0xBMp!L%!|bpXz71ozIg$y!0GF|90%~3@$cpiQLVehjFz~AEWfm zh->wlVO-KI-Dvov>Dq3sSqo|ZeUHc6_EF)1%VhZ71{KDlPRBiCiX308DIih1Lgs>X z1#H&QxuyC79yQF9uGlE&47|bZU9`e!0iQ33%y0&Xd)QO~0 z+eshQk($wQg)yw3a`BYeFd2t=dXM6W7PxA+9j^T9Y&bcnTYMJG9H;o$4 zO5|iXwt6{kWt5uhRcbEdqkf%}sy)Z?CChOwr&J}YR3+!5zF&~a%5$vuaTN2N813U? zG*9bIy*%sP!i-l7OD9bohlZCub)wI5nEUb`~~?!_k#cc delta 1909 zcmbW2Ur19?9LLYSyStmtxvkB=_%BPha&5cQ&8g{*V-gdaf?zE)UlvG&51~cSgH#L& zG4j_#1vOVrLYJ7ghX@4`pG2aUdI;?$iwL1!j0nljcD06_*`J4Tf9H43=iKk_`#X2% z`e*k07F&vV#Ih$*0mCZZAqFXs9+jc|LuANO=JwovfX?fm%ytTdC=kKt0AFM*05+PpA zq9BZOZYBuXysIr^JB9dc79o_Ol_A7jB@sfVJievgX0bcWC&;75VQy@X51j(ZG%)2_0k_Fuc%X z@Efe+oWV;az>sq*BdKKql;q|nC+sAVr`+i&XjEAvDurriOuh*+zh8Wk)1ju8kzW>L zfHz_Y8Kp87%01vU3W+inj48_4JVdCE6&QNV5;a!gfpW7%jhRzWZ!LscvSDZzBr0U{ zfKrgCkdT6!ubg6F%7MY+w8CnY4tgC8uM3GIuB576USWSUD`S!(VF2}546={ zcv&mG%U@Sbr*791io+6h#Z8=WV_0-cln1AvhCGD&=9Q=mUJtmu7@m=F{3tk^QcwfO z2(|7Lpv%_^W*>%_PY?^6>Bm6qYl%{C4$*wR*hKB@>AN>%w27Jf6AYXkJqjPfUYH1D zI85U4(Gd9`#;_iVcSZ%}5i%E%Dlk2`NZm-Bi9V#;*z+h%oq+sF>IEe(PZrWIrp;J{ LR)aYG_)XeRLm+~f diff --git a/testdata/algo/device_extract_au4_with_rec_nets.gds b/testdata/algo/device_extract_au4_with_rec_nets.gds index 7f0110515f3ba96ed0deab151e7b093c384df61d..bd142cbeddf8cbfc4a4bd0f1d9ad3d167d3c6764 100644 GIT binary patch delta 1293 zcmZ{iUr19?9LMk7{4-rQ+tfAcUTlM?2XnJItFi75w^feVkeQKYp{dY=MD!B&B!m&m ziu@Mn2qgj^q(*r*A%kV$LqtXR5HjXd1wBO%B@q#wd0%???cv8czwejN`F-!X=l6YU z>YCLnRY?}hX-SnW)t@9;s*@U}2Fd#W{;qd-Jtg;^CFM8kl#C^r*5@r}Fm8>KOwrnd zT*V6MO;q+^xQh0|)jepmWl64R6KB>e<7X|+zO_%|%N9CMSeZumR?_1W^`jPQAa}c- z%II)2yxz`m!|j8nDyVP=u&yb5#l457{_YS|i^lLxWw@x7Cjh8OEZm zn|GT(Vvfj1573b9xPaPX499Mo#N}9ElU(R9$%R-M^3MrD?oBX!jWb+GnDkuSq~;Q3 zsPCPEQo9(6Nrt|zRwzjaW|BUBt1C(;{M_AKi_U%n>AGg<{Z4!k`^kQWf*wHSKpd+B z43nag=NX)$Rz;O^9370qIVgHj>7?j~L_Z|@p#a9kx~|#zw&Bs87@Jmgm42q;$UPRiWk|b=)T) delta 2036 zcmbW2U1(Eh7{}j}oSu_5jZJL&q4R6qY^#lFPSeC#*Y@>ribQNJsIh6Q!iZ2rys}DR z7aev*M3n76K`%t7wP;RJ*Q47Q7DTpzBEt*q#i(PrDr5A*3l$kM_aw)UAaDHG#Y5ib zdCu=W|K}sg{=LqB4m+!u!W@n+HcxV>e=>=wtb$cCPwx54s>P+EaQ9|m`1&1D7(Ytl zc-qk;O=2?9;$V~LHCrG%+k_YNI+N&ka;x7dOTq^Z$Vr@*xpi93^T6eTByJZBqD;9c zykJU6;*m0lh{!gj`nryx5hgX4O>5MWU`Af#p7TpjLNN23gt?yv-o+4 zDdi5vSh3g1n6#HpjMdIj);5QN4Uxbm`dASOtXiLOqZ&n48FXx15nCQ%O!kpIzj_;u zAQn>@n^%x`5nj_3ehH@#RWrIqWs$CD&;I@F4Z5;oY+YH2uj}eH-$hsFt9~`)=-M|x z%}!ymKqSm=SkZ(3*)Z48dkWKXggsU=4cj*$(q%U+udcT>Y|4x+c=I5wv7!NoXq~N? z=YoXV5{@$^^=K~jV0xB;J~~hMJ=SzdjhI?6o2Y~S4B^8pKgX}793B38Yr<~`3HU3% zF3uU5hDDW?xq$`>%yGS#DN5XBU%6HH3!lCdl>gBBwj$vXmN9qnm;|MUqw5R1jH}f; zj2bOl#?_iUWjtL%)L(TReF2*qsnby%u&I$i9_sfXQP)EpYc!i04(V`dHZ`o}p{|6_ zNjMkb2uAC1r_qOcPZ+3)IDxm-*g zX(meMHg)UsaF*6vb^O$7lRD2s9?*$=V%X$vLr2Wu_<{V)Iz-#@P=l+9nu%-J9AAe( zoMSAmS@YM3SHaq~Hc*5=5*#NIIzHXRQAKu_EFB4BV^=$>y2vLxN%q(Xqb%8uyGf4e zI?8ubzMJx7zl6x|;V2`U@Vl)OJp&&Iw0L_$4JcXNvbKNVyQ7y*NvF6f0huBpNB)6s98#=Kd~`!rO0OexiGqJ zb5TykmZq&s>x(QYA%V7pAVH|7HG$s9vXoMy6t9ZpuC13+q;6ZNNx$y&A{Vn4!!tAA z%sjk1vuEKu{fm2gz2|^O)A~KNLaX@2BRq#a&wGw~%AW0iR{wIfYIBATX}M`_bVLnMNn?2 z&6kXum_Dqmx%ZF0Q>Hxh)nhB}^%rjXl*NX|1U4E8nZ|4Iz3D^7?-m*Jhv4h+A=c!; z2oQ{ugpDQ(Z!mufn8+|=0UMhqPZif?0wJvT`&^^>&vK>cSTKRLAi)VrkrR?43P}-# zorMT3LQ#jvC?s}hoKzV7u4knSg%r|{m zri@*YDUB;Kr7`4SV~AfyoL|t8g(Jg+Pva(HOe1b%VOXY&#bwHk;Swo-P9!mVm0(?! zA}c9HT2hL%Q3ttEeiLJa*-;DnIALzggvHdxY@i`=z$n=8?L!@|yoChjvQ zZV*~NBJ41inG@VvKISoVnK{9&PTWy$@mGl# Tm7n%(f3-NT-14wmY#sa;)jiv% delta 2369 zcmbVOT})eL7(SPGK~sL0B4Ln7&S`TsBx0{23Uk1R0xt*A*6ZWrfCqX zrco4{-$K|ts=aGAvX!o9eS&0`fZuF&cp+P&*pC~;zs<_Sh^?O1d8j3PGn;Mor|kFn z+X&Nf6?+UGNI+@Z|06MxE=`|;*Mgj2p8#VlXs>Ea%~Qf72tDT`10TK!kPI-m5*wcA(G zPQM&!`$dgY|7X7(|J7IhE?=`LUty-Noqp}%>!11+w)yOP&r$n6=k*Ncw-n0z+_I~a z^)uldzA(?4FYHRXQb`LXAKy{bSqkB>m~x&B!IOFl9af8Wz&f4FeA|3U6e}ev5_T0U zb`2k%%*9G6afghsL&Qp%0?#*fseE6VgoHzay-dbznFxEi8@(O5TB3Y2lPP|!GKyrS zis{2Dl81HiWEJsh8G&jMNvDEfjV|8llrUW_!RnL|cZ#sq=;G5gX~aKpN3rNuG2v0M z=+VU&8;Cc`P#Q%ndKI+#baB}$VWLrj!7F3ZE5hK@#V34e#NQTUm={%yHLI9!*2U)~ z;w>_KEh6S+1x?3v@kUv~Sc`-Ws=X$Q*pPMcv14h()e})v6?tH_TQIi^llFaBFLJ?J zs^g6d`!H1O!rz#r{M<?)IpS)1T8iZ#4wDbt8 zfTlZ9MDD04Usn-X*HWKYm3u0d@99lh{)702jIIq4%bN;9TTKXT z%DA#2p@8tcO%VlKG>t74Yc>yd?p47y-KP|{AVxS{whvC5i_n6=Hce?;LD&5wSi0Yk zG8FvNYht Date: Tue, 15 Jan 2019 23:03:25 +0100 Subject: [PATCH 197/335] WIP: added concept of device model. --- src/db/db/db.pro | 6 +- src/db/db/dbCircuit.cc | 11 ++ src/db/db/dbCircuit.h | 1 + src/db/db/dbDevice.cc | 33 ++--- src/db/db/dbDevice.h | 66 ++++----- src/db/db/dbDeviceModel.cc | 87 ++++++++++++ src/db/db/dbDeviceModel.h | 142 +++++++++++++++++++ src/db/db/dbNetlist.cc | 21 +++ src/db/db/dbNetlist.h | 49 +++++++ src/db/db/dbNetlistDeviceExtractor.cc | 14 +- src/db/db/dbNetlistDeviceExtractor.h | 2 +- src/db/db/dbNetlistExtractor.cc | 1 - src/db/db/gsiDeclDbNetlist.cc | 30 ++++ src/db/unit_tests/dbNetlistExtractorTests.cc | 10 +- src/db/unit_tests/dbNetlistTests.cc | 46 +++++- 15 files changed, 444 insertions(+), 75 deletions(-) create mode 100644 src/db/db/dbDeviceModel.cc create mode 100644 src/db/db/dbDeviceModel.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index f45a74d1a..8401e0aef 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -160,7 +160,8 @@ SOURCES = \ dbSubCircuit.cc \ dbPin.cc \ dbLayoutToNetlistReader.cc \ - dbLayoutToNetlistWriter.cc + dbLayoutToNetlistWriter.cc \ + dbDeviceModel.cc HEADERS = \ dbArray.h \ @@ -285,7 +286,8 @@ HEADERS = \ dbPin.h \ dbSubCircuit.h \ dbLayoutToNetlistReader.h \ - dbLayoutToNetlistWriter.h + dbLayoutToNetlistWriter.h \ + dbDeviceModel.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index 7acd9c544..4fff06e8c 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -330,6 +330,17 @@ void Circuit::translate_device_classes (const std::map &map) +{ + for (device_iterator i = m_devices.begin (); i != m_devices.end (); ++i) { + if (i->device_model ()) { + std::map::const_iterator m = map.find (i->device_model ()); + tl_assert (m != map.end ()); + i->set_device_model (m->second); + } + } +} + void Circuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter) { if (m_pin_refs.size () < pin_id + 1) { diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index 98c01ecdb..14426b3f3 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -620,6 +620,7 @@ private: void translate_circuits (const std::map &map); void translate_device_classes (const std::map &map); + void translate_device_models (const std::map &map); void set_netlist (Netlist *netlist); bool combine_parallel_devices (const db::DeviceClass &cls); bool combine_serial_devices (const db::DeviceClass &cls); diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc index 8e0f6ce54..65cacbad4 100644 --- a/src/db/db/dbDevice.cc +++ b/src/db/db/dbDevice.cc @@ -31,7 +31,7 @@ namespace db // Device class implementation Device::Device () - : mp_device_class (0), m_cell_index (std::numeric_limits::max ()), m_id (0), mp_circuit (0) + : mp_device_class (0), mp_device_model (0), m_id (0), mp_circuit (0) { // .. nothing yet .. } @@ -46,13 +46,19 @@ Device::~Device () } Device::Device (DeviceClass *device_class, const std::string &name) - : mp_device_class (device_class), m_name (name), m_cell_index (std::numeric_limits::max ()), m_id (0), mp_circuit (0) + : mp_device_class (device_class), mp_device_model (0), m_name (name), m_id (0), mp_circuit (0) +{ + // .. nothing yet .. +} + +Device::Device (DeviceClass *device_class, DeviceModel *device_model, const std::string &name) + : mp_device_class (device_class), mp_device_model (device_model), m_name (name), m_id (0), mp_circuit (0) { // .. nothing yet .. } Device::Device (const Device &other) - : tl::Object (other), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), m_id (0), mp_circuit (0) + : tl::Object (other), mp_device_class (0), mp_device_model (0), m_id (0), mp_circuit (0) { operator= (other); } @@ -62,10 +68,9 @@ Device &Device::operator= (const Device &other) if (this != &other) { m_name = other.m_name; m_position = other.m_position; - m_cell_index = other.m_cell_index; - m_terminal_cluster_ids = other.m_terminal_cluster_ids; m_parameters = other.m_parameters; mp_device_class = other.mp_device_class; + mp_device_model = other.mp_device_model; } return *this; } @@ -88,24 +93,6 @@ void Device::set_position (const db::DPoint &pt) m_position = pt; } -void Device::set_cell_index (db::cell_index_type ci) -{ - m_cell_index = ci; -} - -size_t Device::cluster_id_for_terminal (size_t terminal_id) const -{ - return terminal_id < m_terminal_cluster_ids.size () ? m_terminal_cluster_ids [terminal_id] : 0; -} - -void Device::set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id) -{ - if (m_terminal_cluster_ids.size () <= terminal_id) { - m_terminal_cluster_ids.resize (terminal_id + 1, 0); - } - m_terminal_cluster_ids [terminal_id] = cluster_id; -} - void Device::set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter) { if (m_terminal_refs.size () < terminal_id + 1) { diff --git a/src/db/db/dbDevice.h b/src/db/db/dbDevice.h index 8074e66fb..b3f6109c9 100644 --- a/src/db/db/dbDevice.h +++ b/src/db/db/dbDevice.h @@ -36,6 +36,7 @@ namespace db class Circuit; class DeviceClass; +class DeviceModel; /** * @brief An actual device @@ -63,6 +64,11 @@ public: */ Device (DeviceClass *device_class, const std::string &name = std::string ()); + /** + * @brief The constructor + */ + Device (DeviceClass *device_class, DeviceModel *device_model, const std::string &name = std::string ()); + /** * @brief Copy constructor */ @@ -86,6 +92,29 @@ public: return mp_device_class; } + /** + * @brief Sets the device class + */ + void set_device_class (DeviceClass *dc) + { + mp_device_class = dc; + } + /** + * @brief Gets the device model + */ + const DeviceModel *device_model () const + { + return mp_device_model; + } + + /** + * @brief Sets the device model + */ + void set_device_model (DeviceModel *dm) + { + mp_device_model = dm; + } + /** * @brief Gets the device ID * The ID is a unique integer which identifies the device. @@ -145,32 +174,6 @@ public: return m_position; } - /** - * @brief Sets the device cell index - * In the layout, a device is represented by a cell. This attribute gives the index of this - * cell. - */ - void set_cell_index (db::cell_index_type ci); - - /** - * @brief Gets the device cell index - */ - db::cell_index_type cell_index () const - { - return m_cell_index; - } - - /** - * @brief Gets the cluster ID for a given terminal - * This attribute connects the device terminal with a terminal cluster - */ - size_t cluster_id_for_terminal (size_t terminal_id) const; - - /** - * @brief Sets the cluster ID for a given terminal - */ - void set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id); - /** * @brief Gets the net attached to a specific terminal * Returns 0 if no net is attached. @@ -223,11 +226,10 @@ private: friend class Net; DeviceClass *mp_device_class; + DeviceModel *mp_device_model; std::string m_name; db::DPoint m_position; - db::cell_index_type m_cell_index; std::vector m_terminal_refs; - std::vector m_terminal_cluster_ids; std::vector m_parameters; size_t m_id; Circuit *mp_circuit; @@ -237,14 +239,6 @@ private: */ void set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter); - /** - * @brief Sets the device class - */ - void set_device_class (DeviceClass *dc) - { - mp_device_class = dc; - } - /** * @brief Sets the device ID */ diff --git a/src/db/db/dbDeviceModel.cc b/src/db/db/dbDeviceModel.cc new file mode 100644 index 000000000..e33d9dc3d --- /dev/null +++ b/src/db/db/dbDeviceModel.cc @@ -0,0 +1,87 @@ + +/* + + 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 "dbDeviceModel.h" +#include "dbCircuit.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// DeviceModel class implementation + +DeviceModel::~DeviceModel () +{ + // .. nothing yet .. +} + +DeviceModel::DeviceModel (const std::string &name) + : m_name (name), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) +{ + // .. nothing yet .. +} + +DeviceModel::DeviceModel (const DeviceModel &other) + : tl::Object (other), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) +{ + operator= (other); +} + +DeviceModel &DeviceModel::operator= (const DeviceModel &other) +{ + if (this != &other) { + m_name = other.m_name; + m_cell_index = other.m_cell_index; + m_terminal_cluster_ids = other.m_terminal_cluster_ids; + } + return *this; +} + +void DeviceModel::set_netlist (Netlist *netlist) +{ + mp_netlist = netlist; +} + +void DeviceModel::set_name (const std::string &n) +{ + m_name = n; +} + +void DeviceModel::set_cell_index (db::cell_index_type ci) +{ + m_cell_index = ci; +} + +size_t DeviceModel::cluster_id_for_terminal (size_t terminal_id) const +{ + return terminal_id < m_terminal_cluster_ids.size () ? m_terminal_cluster_ids [terminal_id] : 0; +} + +void DeviceModel::set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id) +{ + if (m_terminal_cluster_ids.size () <= terminal_id) { + m_terminal_cluster_ids.resize (terminal_id + 1, 0); + } + m_terminal_cluster_ids [terminal_id] = cluster_id; +} + +} diff --git a/src/db/db/dbDeviceModel.h b/src/db/db/dbDeviceModel.h new file mode 100644 index 000000000..1b0406179 --- /dev/null +++ b/src/db/db/dbDeviceModel.h @@ -0,0 +1,142 @@ + +/* + + 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_dbDeviceModel +#define _HDR_dbDeviceModel + +#include "dbCommon.h" +#include "dbNet.h" +#include "dbPoint.h" + +#include "tlObject.h" + +#include + +namespace db +{ + +class Netlist; + +/** + * @brief A device model + * + * A device model represents the geometrical properties of a device. It basically links + * to a cell and clusters for indicating the terminal geometry of the device. + */ +class DB_PUBLIC DeviceModel + : public tl::Object +{ +public: + /** + * @brief The constructor + */ + DeviceModel (const std::string &name = std::string ()); + + /** + * @brief Copy constructor + */ + DeviceModel (const DeviceModel &other); + + /** + * @brief Assignment + */ + DeviceModel &operator= (const DeviceModel &other); + + /** + * @brief Destructor + */ + ~DeviceModel (); + + /** + * @brief Gets the netlist the device lives in (const version) + * This pointer is 0 if the device model isn't added to a netlist + */ + const Netlist *netlist () const + { + return mp_netlist; + } + + /** + * @brief Gets the netlist the device lives in (non-const version) + * This pointer is 0 if the device model isn't added to a netlist + */ + Netlist *netlist () + { + return mp_netlist; + } + + /** + * @brief Sets the name + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the device cell index + * In the layout, a device is represented by a cell. This attribute gives the index of this + * cell. + */ + void set_cell_index (db::cell_index_type ci); + + /** + * @brief Gets the device cell index + */ + db::cell_index_type cell_index () const + { + return m_cell_index; + } + + /** + * @brief Gets the cluster ID for a given terminal + * This attribute connects the device terminal with a terminal cluster + */ + size_t cluster_id_for_terminal (size_t terminal_id) const; + + /** + * @brief Sets the cluster ID for a given terminal + */ + void set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id); + +private: + friend class Netlist; + + std::string m_name; + db::cell_index_type m_cell_index; + std::vector m_terminal_cluster_ids; + Netlist *mp_netlist; + + /** + * @brief Sets the netlist + */ + void set_netlist (Netlist *netlist); +}; + +} + +#endif diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 8a5135d05..d48fb90bf 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -68,10 +68,18 @@ Netlist &Netlist::operator= (const Netlist &other) m_device_classes.push_back (dc_new); } + std::map dmt; + for (const_device_model_iterator dm = other.begin_device_models (); dm != other.end_device_models (); ++dm) { + DeviceModel *dm_new = new DeviceModel (*dm); + dmt [dm.operator-> ()] = dm_new; + m_device_models.push_back (dm_new); + } + std::map ct; for (const_circuit_iterator i = other.begin_circuits (); i != other.end_circuits (); ++i) { Circuit *ct_new = new Circuit (*i); ct_new->translate_device_classes (dct); + ct_new->translate_device_models (dmt); ct [i.operator-> ()] = ct_new; add_circuit (ct_new); } @@ -332,6 +340,7 @@ Netlist::const_bottom_up_circuit_iterator Netlist::end_bottom_up () const void Netlist::clear () { m_device_classes.clear (); + m_device_models.clear (); m_circuits.clear (); } @@ -359,6 +368,18 @@ void Netlist::remove_device_class (DeviceClass *device_class) m_device_classes.erase (device_class); } +void Netlist::add_device_model (DeviceModel *device_model) +{ + m_device_models.push_back (device_model); + device_model->set_netlist (this); +} + +void Netlist::remove_device_model (DeviceModel *device_model) +{ + device_model->set_netlist (0); + m_device_models.erase (device_model); +} + void Netlist::purge_nets () { for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 36dc2676c..739c7888a 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -26,6 +26,7 @@ #include "dbCommon.h" #include "dbCircuit.h" #include "dbDeviceClass.h" +#include "dbDeviceModel.h" #include "tlVector.h" @@ -51,6 +52,9 @@ public: typedef tl::shared_collection device_class_list; typedef device_class_list::const_iterator const_device_class_iterator; typedef device_class_list::iterator device_class_iterator; + typedef tl::shared_collection device_model_list; + typedef device_model_list::const_iterator const_device_model_iterator; + typedef device_model_list::iterator device_model_iterator; typedef tl::vector::const_iterator top_down_circuit_iterator; typedef tl::vector::const_iterator const_top_down_circuit_iterator; typedef tl::vector::const_reverse_iterator bottom_up_circuit_iterator; @@ -301,6 +305,50 @@ public: return m_device_classes.end (); } + /** + * @brief Adds a device model to this netlist + * + * The netlist takes over ownership of the object. + */ + void add_device_model (DeviceModel *device_model); + + /** + * @brief Deletes a device model from the netlist + */ + void remove_device_model (DeviceModel *device_model); + + /** + * @brief Begin iterator for the device models of the netlist (non-const version) + */ + device_model_iterator begin_device_models () + { + return m_device_models.begin (); + } + + /** + * @brief End iterator for the device models of the netlist (non-const version) + */ + device_model_iterator end_device_models () + { + return m_device_models.end (); + } + + /** + * @brief Begin iterator for the device models of the netlist (const version) + */ + const_device_model_iterator begin_device_models () const + { + return m_device_models.begin (); + } + + /** + * @brief End iterator for the device models of the netlist (const version) + */ + const_device_model_iterator end_device_models () const + { + return m_device_models.end (); + } + /** * @brief Purge unused nets * @@ -338,6 +386,7 @@ private: circuit_list m_circuits; device_class_list m_device_classes; + device_model_list m_device_models; bool m_valid_topology; int m_lock_count; tl::vector m_top_down_circuits; diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 74b38e3b9..0f12dd3c8 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -281,12 +281,17 @@ void NetlistDeviceExtractor::push_new_devices () db::PropertiesRepository::properties_set ps; - std::map::iterator c = m_device_cells.find (key); + std::map >::iterator c = m_device_cells.find (key); if (c == m_device_cells.end ()) { std::string cell_name = "D$" + mp_device_class->name (); db::Cell &device_cell = mp_layout->cell (mp_layout->add_cell (cell_name.c_str ())); - c = m_device_cells.insert (std::make_pair (key, device_cell.cell_index ())).first; + + db::DeviceModel *dm = new db::DeviceModel (mp_layout->cell_name (device_cell.cell_index ())); + m_netlist->add_device_model (dm); + dm->set_cell_index (device_cell.cell_index ()); + + c = m_device_cells.insert (std::make_pair (key, std::make_pair (device_cell.cell_index (), dm))).first; // attach the device class ID to the cell ps.clear (); @@ -305,6 +310,7 @@ void NetlistDeviceExtractor::push_new_devices () // initialize the local cluster (will not be extracted) db::local_cluster *lc = cc.insert (); lc->add_attr (pi); + dm->set_cluster_id_for_terminal (t->first, lc->id ()); // build the cell shapes and local cluster for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) { @@ -322,14 +328,14 @@ void NetlistDeviceExtractor::push_new_devices () } // make the cell index known to the device - device->set_cell_index (c->second); + device->set_device_model (c->second.second); // Build a property set for the device ID ps.clear (); ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (d->first))); db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); - db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (c->second), db::Trans (disp)), pi); + db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (c->second.first), db::Trans (disp)), pi); mp_layout->cell (m_cell_index).insert (inst); } diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index 3626e4876..0cc045ceb 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -513,7 +513,7 @@ private: std::vector m_layers; error_list m_errors; std::map m_new_devices; - std::map m_device_cells; + std::map > m_device_cells; // no copying NetlistDeviceExtractor (const NetlistDeviceExtractor &); diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 2d0b95248..8f88932e2 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -208,7 +208,6 @@ void NetlistExtractor::connect_devices (db::Circuit *circuit, size_t tid = j->second.to (); device->connect_terminal (tid, net); - device->set_cluster_id_for_terminal (tid, i->id ()); } diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index c088750cd..1009f26fd 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -134,6 +134,36 @@ Class decl_dbDevice ("db", "Device", "This class has been added in version 0.26." ); +#if 0 +// TODO: activate once the geometry API is opened (clusters at al.) +Class decl_dbDeviceModel ("db", "DeviceModel", + gsi::method ("netlist", (db::Netlist *(db::DeviceModel::*) ()) &db::DeviceModel::netlist, + "@brief Gets the netlist the device model lives in." + ) + + gsi::method ("name=", &db::DeviceModel::set_name, gsi::arg ("name"), + "@brief Sets the name of the device model.\n" + "Device names are used to name a device model inside a netlist file. " + "Device names should be unique within a netlist." + ) + + gsi::method ("name", &db::DeviceModel::name, + "@brief Gets the name of the device model.\n" + ) + + gsi::method ("cell_index", &db::DeviceModel::cell_index, + "@brief Gets the cell index of the device model.\n" + "This is the cell that represents the device." + ) + + gsi::method ("cluster_id_for_terminal", &db::DeviceModel::cluster_id_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the cluster ID for the given terminal.\n" + "The cluster ID links the terminal to geometrical shapes within the clusters of the cell (see \\cell_index)" + ), + "@brief A geometrical device model\n" + "This class represents the geometrical model for the device. It links into the extracted layout " + "to a cell which holds the terminal shapes for the device.\n" + "\n" + "This class has been added in version 0.26." +); +#endif + static void subcircuit_connect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin, db::Net *net) { if (pin) { diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 44d09a4a9..4eab0b589 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -93,12 +93,14 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters< for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { - if (device_cells_seen.find (d->cell_index ()) != device_cells_seen.end ()) { + db::cell_index_type dci = d->device_model ()->cell_index (); + + if (device_cells_seen.find (dci) != device_cells_seen.end ()) { continue; } - db::Cell &device_cell = ly.cell (cmap.cell_mapping (d->cell_index ())); - device_cells_seen.insert (d->cell_index ()); + db::Cell &device_cell = ly.cell (cmap.cell_mapping (dci)); + device_cells_seen.insert (dci); std::string ps; const std::vector &pd = d->device_class ()->parameter_definitions (); @@ -112,7 +114,7 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters< const std::vector &td = d->device_class ()->terminal_definitions (); for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { - const db::local_cluster &dc = clusters.clusters_per_cell (d->cell_index ()).cluster_by_id (d->cluster_id_for_terminal (t->id ())); + const db::local_cluster &dc = clusters.clusters_per_cell (dci).cluster_by_id (d->device_model ()->cluster_id_for_terminal (t->id ())); for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { db::Shapes &target = device_cell.shapes (m->second); diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index ab1e4df29..91a2f5058 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -124,7 +124,11 @@ static std::string netlist2 (const db::Circuit &c) pins += "="; pins += net ? net->name () : std::string ("(null)"); } - res += " D" + d->name () + ":" + pins + "\n"; + res += " D" + d->name (); + if (d->device_model ()) { + res += "/" + d->device_model ()->name (); + } + res += ":" + pins + "\n"; } for (db::Circuit::const_subcircuit_iterator s = c.begin_subcircuits (); s != c.end_subcircuits (); ++s) { @@ -497,6 +501,12 @@ TEST(4_NetlistSubcircuits) dc->add_terminal_definition (db::DeviceTerminalDefinition ("B", "")); nl->add_device_class (dc); + db::DeviceModel *dm = new db::DeviceModel (); + dm->set_name ("dm2"); + dm->set_cell_index (42); + dm->set_cluster_id_for_terminal (0, 17); + nl->add_device_model (dm); + db::Circuit *c1 = new db::Circuit (); c1->set_cell_index (17); EXPECT_EQ (c1->netlist (), 0); @@ -526,8 +536,9 @@ TEST(4_NetlistSubcircuits) EXPECT_EQ (nl->circuit_by_cell_index (41) == 0, true); EXPECT_EQ (nl->circuit_by_cell_index (42) == c2, true); - db::Device *d = new db::Device (dc, "D"); + db::Device *d = new db::Device (dc, dm, "D"); c2->add_device (d); + EXPECT_EQ (d->device_model ()->name (), "dm2"); EXPECT_EQ (refs2string (c2), ""); db::SubCircuit *sc1 = new db::SubCircuit (c2); @@ -601,7 +612,7 @@ TEST(4_NetlistSubcircuits) " Xsc1:c2p1=n1a,c2p2=n1b\n" " Xsc2:c2p1=n1b,c2p2=n1c\n" "c2:c2p1=n2a,c2p2=n2b\n" - " DD:A=n2a,B=n2b\n" + " DD/dm2:A=n2a,B=n2b\n" ); // check netlist @@ -636,7 +647,7 @@ TEST(4_NetlistSubcircuits) " Xsc1:c2p1=n1a,c2p2=n1b\n" " Xsc2:c2p1=n1b,c2p2=n1c\n" "c2:c2p1=n2a,c2p2=n2b\n" - " DD:A=n2a,B=n2b\n" + " DD/dm2:A=n2a,B=n2b\n" ); // check netlist @@ -1028,3 +1039,30 @@ TEST(12_NetlistTopology) EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2"); EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1"); } + +TEST(13_DeviceModel) +{ + db::Netlist nl; + + db::DeviceModel *dm = new db::DeviceModel ("name"); + EXPECT_EQ (dm->name (), "name"); + dm->set_name ("name2"); + EXPECT_EQ (dm->name (), "name2"); + + dm->set_cluster_id_for_terminal (1, 17); + dm->set_cluster_id_for_terminal (0, 42); + EXPECT_EQ (dm->cluster_id_for_terminal (0), size_t (42)); + EXPECT_EQ (dm->cluster_id_for_terminal (1), size_t (17)); + + dm->set_cell_index (5); + EXPECT_EQ (dm->cell_index (), db::cell_index_type (5)); + + nl.add_device_model (dm); + EXPECT_EQ (dm->netlist () == &nl, true); + EXPECT_EQ (nl.begin_device_models () == nl.end_device_models (), false); + EXPECT_EQ (nl.begin_device_models ()->name (), "name2"); + + nl.remove_device_model (dm); + + EXPECT_EQ (nl.begin_device_models () == nl.end_device_models (), true); +} From 438f50091fbd2382d46fabdcfea0b79c1db115a8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 16 Jan 2019 00:49:51 +0100 Subject: [PATCH 198/335] WIP: Refined output format for l2n --- src/db/db/dbDevice.cc | 9 + src/db/db/dbDevice.h | 7 + src/db/db/dbDeviceModel.cc | 13 +- src/db/db/dbDeviceModel.h | 24 +- src/db/db/dbLayoutToNetlistWriter.cc | 212 +++++++++++++----- src/db/db/dbLayoutToNetlistWriter.h | 4 + src/db/db/dbNetlistDeviceExtractor.cc | 2 +- src/db/db/dbPin.cc | 10 + src/db/db/dbPin.h | 6 + src/db/db/dbSubCircuit.cc | 9 + src/db/db/dbSubCircuit.h | 6 + src/db/db/gsiDeclDbLayoutToNetlist.cc | 13 ++ .../dbLayoutToNetlistWriterTests.cc | 174 ++++++++++++++ src/db/unit_tests/dbNetlistTests.cc | 5 +- src/db/unit_tests/unit_tests.pro | 3 +- 15 files changed, 431 insertions(+), 66 deletions(-) create mode 100644 src/db/unit_tests/dbLayoutToNetlistWriterTests.cc diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc index 65cacbad4..e7a6b1cd0 100644 --- a/src/db/db/dbDevice.cc +++ b/src/db/db/dbDevice.cc @@ -75,6 +75,15 @@ Device &Device::operator= (const Device &other) return *this; } +std::string Device::expanded_name () const +{ + if (name ().empty ()) { + return "$" + tl::to_string (id ()); + } else { + return name (); + } +} + void Device::set_circuit (Circuit *circuit) { mp_circuit = circuit; diff --git a/src/db/db/dbDevice.h b/src/db/db/dbDevice.h index b3f6109c9..3a64f492a 100644 --- a/src/db/db/dbDevice.h +++ b/src/db/db/dbDevice.h @@ -99,6 +99,7 @@ public: { mp_device_class = dc; } + /** * @brief Gets the device model */ @@ -157,6 +158,12 @@ public: return m_name; } + /** + * @brief Gets a name which always non-empty + * This method will pick a name like "$" if the explicit name is empty. + */ + std::string expanded_name () const; + /** * @brief Sets the device position * The device position should be the center of the recognition shape or something similar. diff --git a/src/db/db/dbDeviceModel.cc b/src/db/db/dbDeviceModel.cc index e33d9dc3d..69245cc8f 100644 --- a/src/db/db/dbDeviceModel.cc +++ b/src/db/db/dbDeviceModel.cc @@ -29,19 +29,25 @@ namespace db // -------------------------------------------------------------------------------- // DeviceModel class implementation +DeviceModel::DeviceModel () + : m_name (), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) +{ + // .. nothing yet .. +} + DeviceModel::~DeviceModel () { // .. nothing yet .. } -DeviceModel::DeviceModel (const std::string &name) - : m_name (name), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) +DeviceModel::DeviceModel (db::DeviceClass *device_class, const std::string &name) + : m_name (name), mp_device_class (device_class), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) { // .. nothing yet .. } DeviceModel::DeviceModel (const DeviceModel &other) - : tl::Object (other), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) + : tl::Object (other), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) { operator= (other); } @@ -50,6 +56,7 @@ DeviceModel &DeviceModel::operator= (const DeviceModel &other) { if (this != &other) { m_name = other.m_name; + mp_device_class = other.mp_device_class; m_cell_index = other.m_cell_index; m_terminal_cluster_ids = other.m_terminal_cluster_ids; } diff --git a/src/db/db/dbDeviceModel.h b/src/db/db/dbDeviceModel.h index 1b0406179..ac515befe 100644 --- a/src/db/db/dbDeviceModel.h +++ b/src/db/db/dbDeviceModel.h @@ -46,10 +46,15 @@ class DB_PUBLIC DeviceModel : public tl::Object { public: + /** + * @brief Default constructor + */ + DeviceModel (); + /** * @brief The constructor */ - DeviceModel (const std::string &name = std::string ()); + DeviceModel (db::DeviceClass *device_class, const std::string &name = std::string ()); /** * @brief Copy constructor @@ -66,6 +71,22 @@ public: */ ~DeviceModel (); + /** + * @brief Gets the device class + */ + const DeviceClass *device_class () const + { + return mp_device_class; + } + + /** + * @brief Sets the device class + */ + void set_device_class (DeviceClass *dc) + { + mp_device_class = dc; + } + /** * @brief Gets the netlist the device lives in (const version) * This pointer is 0 if the device model isn't added to a netlist @@ -127,6 +148,7 @@ private: friend class Netlist; std::string m_name; + db::DeviceClass *mp_device_class; db::cell_index_type m_cell_index; std::vector m_terminal_cluster_ids; Netlist *mp_netlist; diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 8bad67fe8..0c824e1f4 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -39,49 +39,66 @@ namespace db * non-alpha (e.g. "*") or empty. * Single-valued attributes can be given without brackets. * All dimensions are in units of database unit. + * The file follows the declaration-before-use principle + * (circuits before subcircuits, nets before use ...) * * Global statements: * + * version() - file format version * description() - an arbitrary description text - * dbu() - specifies the database unit [short key: D] + * unit() - specifies the database unit [short key: U] * top() - specifies the name of the top circuit [short key: T] * layer() - define a layer [short key: L] * connect( ...) - connects layer1 with the following layers [short key: C] * global( ...) - connects a layer with the given global nets [short key: G] - * circuit( ...) - defines a circuit (cell) [short key: X] + * circuit( [circuit-def]) - circuit (cell) [short key: X] + * device( [device-footprint-def]) + * - device footprint [short key: D] * - * Inside the circuit: + * [circuit-def]: * - * net( ...) - specifies net geometry [short key: N] - * device( ...) - defines a device [short key: D] - * subcircuit( ...) - defines a subcircuit [short key: X] + * net( [geometry-def]) - net geometry [short key: N] + * A net declaration shall be there also if no geometry + * is present + * pin( ) - outgoing pin connection [short key: P] + * device( [device-def]) + * - device with connections [short key: D] + * subcircuit( [subcircuit-def]) + * - subcircuit with connections [short key: X] * - * Inside a net: + * [geometry-def]: * * polygon( ...) - defines a polygon [short key: P] * rect( ) * - defines a rectangle [short key: R] * - * Inside a device: + * [device-footprint-def]: + * + * terminal( [geometry-def]) + * - specifies the terminal geometry [short key: empty] + * + * [device-def]: * * param( ) - defines a parameter [short key P] + * footprint() - links to a geometrical device footprint on top level [short key F] * terminal( ) * - specifies connection of the terminal with * a net (short key: empty) * location( ) - location of the device [short key L] * - * Inside a subcircuit: + * [subcircuit-def]: * * location( ) - location of the subcircuit [short key L] * rotation() - rotation angle [short key O] * mirror - if specified, the instance is mirrored before rotation [short key M] * scale() - magnification [short key *] - * pin( ) - specifies connection of the pin with a net + * pin( ) - specifies connection of the pin with a net [short key: P] */ +static std::string version_key ("version"); static std::string description_key ("description"); static std::string top_key ("top"); -static std::string dbu_key ("dbu"); +static std::string unit_key ("unit"); static std::string layer_key ("layer"); static std::string text_key ("text"); static std::string connect_key ("connect"); @@ -93,6 +110,7 @@ static std::string subcircuit_key ("subcircuit"); static std::string polygon_key ("polygon"); static std::string rect_key ("rect"); static std::string terminal_key ("terminal"); +static std::string footprint_key ("footprint"); static std::string label_key ("label"); static std::string param_key ("param"); static std::string location_key ("location"); @@ -122,12 +140,17 @@ static std::string name_for_layer (const db::Layout *layout, unsigned int l) void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) { + const int version = 1; + const db::Layout *ly = l2n->internal_layout (); const db::Netlist *nl = l2n->netlist (); + *mp_stream << "# General" << endl; + *mp_stream << version_key << "(" << version << ")" << endl; *mp_stream << top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; - *mp_stream << dbu_key << "(" << ly->dbu () << ")" << endl; + *mp_stream << unit_key << "(" << ly->dbu () << ")" << endl; + *mp_stream << endl << "# Layers" << endl; for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { *mp_stream << layer_key << "(" << name_for_layer (ly, *l) << ")" << endl; @@ -137,7 +160,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) if (cb != ce) { *mp_stream << connect_key << "(" << name_for_layer (ly, *l); for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) { - *mp_stream << " " << name_for_layer (ly, *l); + *mp_stream << " " << name_for_layer (ly, *c); } *mp_stream << ")" << endl; } @@ -154,7 +177,18 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) } - for (db::Netlist::const_circuit_iterator x = nl->begin_circuits (); x != nl->end_circuits (); ++x) { + *mp_stream << endl << "# Device footprints" << endl; + for (db::Netlist::const_device_model_iterator m = nl->begin_device_models (); m != nl->end_device_models (); ++m) { + if (m->device_class ()) { + *mp_stream << device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl; + write (l2n, *m); + *mp_stream << ")" << endl; + } + } + + for (db::Netlist::const_top_down_circuit_iterator i = nl->begin_top_down (); i != nl->end_top_down (); ++i) { + const db::Circuit *x = *i; + *mp_stream << endl << "# Circuit " << x->name () << endl; *mp_stream << circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; write (l2n, *x); *mp_stream << ")" << endl; @@ -167,6 +201,13 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const write (l2n, *n); } + for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { + const db::Net *net = circuit.net_for_pin (p->id ()); + if (net) { + *mp_stream << indent1 << pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl; + } + } + for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { write (l2n, *d); } @@ -185,6 +226,31 @@ void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr) } } +void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const std::string &lname) +{ + const db::Polygon &poly = s->obj (); + if (poly.is_box ()) { + + db::Box box = s->trans () * poly.box (); + *mp_stream << rect_key << "(" << lname; + *mp_stream << " " << box.left () << " " << box.bottom (); + *mp_stream << " " << box.right () << " " << box.top (); + *mp_stream << ")"; + + } else { + + *mp_stream << polygon_key << "(" << lname; + if (poly.holes () > 0) { + db::SimplePolygon sp (poly); + write_points (*mp_stream, sp, s->trans ()); + } else { + write_points (*mp_stream, poly, s->trans ()); + } + *mp_stream << ")"; + + } +} + void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Net &net) { const db::Layout *ly = l2n->internal_layout (); @@ -192,47 +258,39 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const const db::Circuit *circuit = net.circuit (); const db::Connectivity &conn = l2n->connectivity (); - *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; + bool any = false; for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { const db::local_cluster &lc = clusters.clusters_per_cell (circuit->cell_index ()).cluster_by_id (net.cluster_id ()); for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { - *mp_stream << indent2; - - const db::Polygon &poly = s->obj (); - if (poly.is_box ()) { - - db::Box box = s->trans () * poly.box (); - *mp_stream << rect_key << "(" << name_for_layer (ly, *l); - *mp_stream << " " << box.left () << " " << box.bottom (); - *mp_stream << " " << box.right () << " " << box.top (); - *mp_stream << ")"; - - } else { - - *mp_stream << polygon_key << "(" << name_for_layer (ly, *l); - if (poly.holes () > 0) { - db::SimplePolygon sp (poly); - write_points (*mp_stream, sp, s->trans ()); - } else { - write_points (*mp_stream, poly, s->trans ()); - } - *mp_stream << ")"; - + if (! any) { + *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; + any = true; } + *mp_stream << indent2; + write (s.operator-> (), name_for_layer (ly, *l)); + *mp_stream << endl; + } } - *mp_stream << indent1 << ")" << endl; + if (any) { + *mp_stream << indent1 << ")" << endl; + } else { + *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << ")" << endl; + } } -void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, const db::SubCircuit &subcircuit) +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit) { - *mp_stream << indent1 << subcircuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.name ()); + const db::Layout *ly = l2n->internal_layout (); + double dbu = ly->dbu (); + + *mp_stream << indent1 << subcircuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.expanded_name ()); const db::DCplxTrans &tr = subcircuit.trans (); if (tr.is_mag ()) { @@ -244,24 +302,27 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, if (fabs (tr.angle ()) > 1e-6) { *mp_stream << " " << rotation_key << "(" << tr.angle () << ")"; } - *mp_stream << " " << location_key << "(" << tr.disp ().x () << " " << tr.disp ().y () << ")"; + *mp_stream << " " << location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")"; - // each pin in one line for more than 16 pins - bool separate_lines = (subcircuit.circuit ()->pin_count () > 16); + // each pin in one line for more than a few pins + bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1); if (separate_lines) { *mp_stream << endl; } - for (db::Circuit::const_pin_iterator p = subcircuit.circuit ()->begin_pins (); p != subcircuit.circuit ()->end_pins (); ++p) { - if (separate_lines) { - *mp_stream << indent2; - } else { - *mp_stream << " "; - } - *mp_stream << pin_key << "(" << tl::to_word_or_quoted_string (p->name ()) << " " << tl::to_word_or_quoted_string (subcircuit.net_for_pin (p->id ())->expanded_name ()) << ")"; - if (separate_lines) { - *mp_stream << endl; + for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) { + const db::Net *net = subcircuit.net_for_pin (p->id ()); + if (net) { + if (separate_lines) { + *mp_stream << indent2; + } else { + *mp_stream << " "; + } + *mp_stream << pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")"; + if (separate_lines) { + *mp_stream << endl; + } } } @@ -272,24 +333,57 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, *mp_stream << ")" << endl; } -void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, const db::Device &device) +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model) { - // @@@ TODO: add location + const std::vector &td = device_model.device_class ()->terminal_definitions (); - *mp_stream << indent1 << device_key << "(" << tl::to_word_or_quoted_string (device.name ()); - *mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()); + const db::Layout *ly = l2n->internal_layout (); + const db::hier_clusters &clusters = l2n->net_clusters (); + const db::Connectivity &conn = l2n->connectivity (); + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + + const db::local_cluster &lc = clusters.clusters_per_cell (device_model.cell_index ()).cluster_by_id (device_model.cluster_id_for_terminal (t->id ())); + for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { + + *mp_stream << indent1; + write (s.operator-> (), name_for_layer (ly, *l)); + *mp_stream << endl; + + } + + } + + } +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Device &device) +{ + const db::Layout *ly = l2n->internal_layout (); + double dbu = ly->dbu (); + + *mp_stream << indent1 << device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ()); + *mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl; + + *mp_stream << indent2 << location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl; + + if (device.device_model ()) { + *mp_stream << indent2 << footprint_key << "(" << tl::to_word_or_quoted_string (device.device_model ()->name ()) << ")" << endl; + } const std::vector &pd = device.device_class ()->parameter_definitions (); for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - *mp_stream << " " << param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")"; + *mp_stream << indent2 << param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl; } const std::vector &td = device.device_class ()->terminal_definitions (); for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { - *mp_stream << " " << terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (device.net_for_terminal (i->id ())->expanded_name ()) << ")"; + *mp_stream << indent2 << terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (device.net_for_terminal (i->id ())->expanded_name ()) << ")" << endl; } - *mp_stream << ")" << endl; + *mp_stream << indent1 << ")" << endl; } } diff --git a/src/db/db/dbLayoutToNetlistWriter.h b/src/db/db/dbLayoutToNetlistWriter.h index f158f8911..2955ba66b 100644 --- a/src/db/db/dbLayoutToNetlistWriter.h +++ b/src/db/db/dbLayoutToNetlistWriter.h @@ -24,6 +24,7 @@ #define HDR_dbLayoutToNetlistWriter #include "dbCommon.h" +#include "dbPolygon.h" #include "tlStream.h" namespace db @@ -34,6 +35,7 @@ class Net; class Circuit; class SubCircuit; class Device; +class DeviceModel; /** * @brief The base class for a LayoutToNetlist writer @@ -65,6 +67,8 @@ private: void write (const db::LayoutToNetlist *l2n, const db::Net &net); void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit); void write (const db::LayoutToNetlist *l2n, const db::Device &device); + void write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model); + void write (const db::PolygonRef *s, const std::string &lname); }; } diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 0f12dd3c8..87db486d6 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -287,7 +287,7 @@ void NetlistDeviceExtractor::push_new_devices () std::string cell_name = "D$" + mp_device_class->name (); db::Cell &device_cell = mp_layout->cell (mp_layout->add_cell (cell_name.c_str ())); - db::DeviceModel *dm = new db::DeviceModel (mp_layout->cell_name (device_cell.cell_index ())); + db::DeviceModel *dm = new db::DeviceModel (mp_device_class, mp_layout->cell_name (device_cell.cell_index ())); m_netlist->add_device_model (dm); dm->set_cell_index (device_cell.cell_index ()); diff --git a/src/db/db/dbPin.cc b/src/db/db/dbPin.cc index 74b98b51d..e223ec8ae 100644 --- a/src/db/db/dbPin.cc +++ b/src/db/db/dbPin.cc @@ -21,6 +21,7 @@ */ #include "dbPin.h" +#include "tlString.h" namespace db { @@ -40,4 +41,13 @@ Pin::Pin (const std::string &name) // .. nothing yet .. } +std::string Pin::expanded_name () const +{ + if (name ().empty ()) { + return "$" + tl::to_string (id ()); + } else { + return name (); + } +} + } diff --git a/src/db/db/dbPin.h b/src/db/db/dbPin.h index 925d6e023..2ee33cf6c 100644 --- a/src/db/db/dbPin.h +++ b/src/db/db/dbPin.h @@ -56,6 +56,12 @@ public: return m_name; } + /** + * @brief Gets a name which always non-empty + * This method will pick a name like "$" if the explicit name is empty. + */ + std::string expanded_name () const; + /** * @brief Gets the ID of the pin (only pins inside circuits have valid ID's) */ diff --git a/src/db/db/dbSubCircuit.cc b/src/db/db/dbSubCircuit.cc index 9de235b97..4db7dfa6c 100644 --- a/src/db/db/dbSubCircuit.cc +++ b/src/db/db/dbSubCircuit.cc @@ -74,6 +74,15 @@ void SubCircuit::set_name (const std::string &n) } } +std::string SubCircuit::expanded_name () const +{ + if (name ().empty ()) { + return "$" + tl::to_string (id ()); + } else { + return name (); + } +} + void SubCircuit::set_trans (const db::DCplxTrans &t) { m_trans = t; diff --git a/src/db/db/dbSubCircuit.h b/src/db/db/dbSubCircuit.h index a28b8211a..6a25001c0 100644 --- a/src/db/db/dbSubCircuit.h +++ b/src/db/db/dbSubCircuit.h @@ -134,6 +134,12 @@ public: return m_name; } + /** + * @brief Gets a name which always non-empty + * This method will pick a name like "$" if the explicit name is empty. + */ + std::string expanded_name () const; + /** * @brief Sets the transformation describing the subcircuit * diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 884ad8f94..3974dddc5 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -22,6 +22,8 @@ #include "gsiDecl.h" #include "dbLayoutToNetlist.h" +#include "dbLayoutToNetlistWriter.h" +#include "tlStream.h" namespace gsi { @@ -58,6 +60,13 @@ static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMappin l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } +static void write_l2n (const db::LayoutToNetlist *l2n, const std::string &path) +{ + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream); + writer.write (l2n); +} + Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::constructor ("new", &make_l2n, gsi::arg ("iter"), "@brief The constructor\n" @@ -254,6 +263,10 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "See the description of the other \\probe_net variant.\n" "This variant accepts a database-unit location. The location is given in the\n" "coordinate space of the initial cell.\n" + ) + + gsi::method_ext ("write", &write_l2n, gsi::arg ("path"), + "@brief Writes the extracted netlist to a file.\n" + "This method employs the native format of KLayout.\n" ), "@brief A generic framework for extracting netlists from layouts\n" "\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc new file mode 100644 index 000000000..0213af5dd --- /dev/null +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -0,0 +1,174 @@ + +/* + + 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 "dbLayoutToNetlist.h" +#include "dbLayoutToNetlistWriter.h" +#include "dbStream.h" +#include "dbCommonReader.h" +#include "dbNetlistDeviceExtractorClasses.h" + +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" + +static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) +{ + unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +TEST(1_WriterBasic) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell)); + std::auto_ptr ractive (l2n.make_layer (active)); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + + // derived regions + + db::Region rpactive = *ractive & *rnwell; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = *ractive - *rnwell; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // return the computed layers into the original layout and write it for debugging purposes + // NOTE: this will include the device layers too + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + unsigned int lpoly = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Poly with gate terminal + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpoly->insert_into (&ly, tc.cell_index (), lpoly); + + // net extraction + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + tl::OutputMemoryStream mem; + { + tl::OutputStream stream (mem); + db::LayoutToNetlistStandardWriter writer (stream); + writer.write (&l2n); + } + +// TODO: too big for inlined text ... +#if 0 + EXPECT_EQ (std::string (mem.data (), mem.size ()), + "" + ); +#endif +} diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 91a2f5058..799d1c791 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -502,6 +502,8 @@ TEST(4_NetlistSubcircuits) nl->add_device_class (dc); db::DeviceModel *dm = new db::DeviceModel (); + dm->set_device_class (dc); + EXPECT_EQ (dm->device_class () == dc, true); dm->set_name ("dm2"); dm->set_cell_index (42); dm->set_cluster_id_for_terminal (0, 17); @@ -1044,7 +1046,8 @@ TEST(13_DeviceModel) { db::Netlist nl; - db::DeviceModel *dm = new db::DeviceModel ("name"); + db::DeviceModel *dm = new db::DeviceModel (0, "name"); + EXPECT_EQ (dm->device_class () == 0, true); EXPECT_EQ (dm->name (), "name"); dm->set_name ("name2"); EXPECT_EQ (dm->name (), "name2"); diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index c0701254d..5bed164ca 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -63,7 +63,8 @@ SOURCES = \ dbNetlistExtractorTests.cc \ dbNetlistDeviceExtractorTests.cc \ dbNetlistDeviceClassesTests.cc \ - dbLayoutToNetlistTests.cc + dbLayoutToNetlistTests.cc \ + dbLayoutToNetlistWriterTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC From 56bb39a273aa487b5efdc2454a748e705b6416da Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 16 Jan 2019 22:45:58 +0100 Subject: [PATCH 199/335] LayoutToNetlist enhancements in the area of the dumper. --- src/db/db/dbHierNetworkProcessor.cc | 21 + src/db/db/dbHierNetworkProcessor.h | 5 + src/db/db/dbLayoutToNetlist.cc | 36 +- src/db/db/dbLayoutToNetlist.h | 25 +- src/db/db/dbLayoutToNetlistWriter.cc | 145 ++++-- src/db/db/dbLayoutToNetlistWriter.h | 2 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 27 +- .../unit_tests/dbHierNetworkProcessorTests.cc | 16 +- .../dbLayoutToNetlistWriterTests.cc | 49 +- testdata/algo/l2n_writer_au.txt | 477 ++++++++++++++++++ 10 files changed, 725 insertions(+), 78 deletions(-) create mode 100644 testdata/algo/l2n_writer_au.txt diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 2f41ea28e..fb2ea01f1 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -2029,6 +2029,27 @@ recursive_cluster_shape_iterator &recursive_cluster_shape_iterator::operat return *this; } +template +void recursive_cluster_shape_iterator::skip_cell () +{ + m_shape_iter = typename db::local_cluster::shape_iterator (); + + do { + + up (); + if (m_conn_iter_stack.empty ()) { + return; + } + + ++m_conn_iter_stack.back ().first; + + } while (m_conn_iter_stack.back ().first == m_conn_iter_stack.back ().second); + + while (m_shape_iter.at_end () && ! m_conn_iter_stack.empty ()) { + next_conn (); + } +} + template void recursive_cluster_shape_iterator::next_conn () { diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index d4211f9ce..535d18752 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -882,6 +882,11 @@ public: */ recursive_cluster_shape_iterator &operator++ (); + /** + * @brief Skips the current cell and advances to the next cell and shape + */ + void skip_cell (); + private: typedef typename db::connected_clusters::connections_type connections_type; diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 773a47c1e..8d8a89381 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -78,28 +78,43 @@ size_t LayoutToNetlist::max_vertex_count () const return m_dss.max_vertex_count (); } -db::Region *LayoutToNetlist::make_layer (unsigned int layer_index) +db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::string &n) { db::RecursiveShapeIterator si (m_iter); si.set_layer (layer_index); si.shape_flags (db::ShapeIterator::All); - return new db::Region (si, m_dss); + + db::Region *region = new db::Region (si, m_dss); + if (! n.empty ()) { + name (*region, n); + } + return region; } -db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index) +db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index, const std::string &n) { db::RecursiveShapeIterator si (m_iter); si.set_layer (layer_index); si.shape_flags (db::ShapeIterator::Texts); - return new db::Region (si, m_dss); + + db::Region *region = new db::Region (si, m_dss); + if (! n.empty ()) { + name (*region, n); + } + return region; } -db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index) +db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const std::string &n) { db::RecursiveShapeIterator si (m_iter); si.set_layer (layer_index); si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes); - return new db::Region (si, m_dss); + + db::Region *region = new db::Region (si, m_dss); + if (! n.empty ()) { + name (*region, n); + } + return region; } void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers) @@ -202,6 +217,15 @@ const db::Cell *LayoutToNetlist::internal_top_cell () const return &m_dss.const_initial_cell (); } +void LayoutToNetlist::name (const db::Region ®ion, const std::string &name) +{ + unsigned int li = layer_of (region); + db::Layout &ly = m_dss.layout (); + db::LayerProperties lp = ly.get_properties (li); + lp.name = name; + ly.set_properties (li, lp); +} + unsigned int LayoutToNetlist::layer_of (const db::Region ®ion) const { const db::DeepRegion *dr = dynamic_cast (region.delegate ()); diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index d2f5649e8..df01397c1 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -114,6 +114,25 @@ public: */ size_t max_vertex_count () const; + /** + * @brief Names a layer + * This is a formal name for the layer. Using a name or layer properties + * (see below) enhances readability of backannotated information + * if layers are involved. Use this method or the other variants to + * attach a name or standard layer properties to a region delivered + * by "make_layer" or derived from other regions through boolean + * operations. + */ + void name (const db::Region ®ion, const std::string &name); + + /** + * @brief Gets the name of the given layer + */ + std::string name (const db::Region ®ion) const + { + return internal_layout ()->get_properties (layer_of (region)).name; + } + /** * @brief Creates a new region representing an original layer * "layer_index" is the layer index of the desired layer in the original layout. @@ -122,19 +141,19 @@ public: * A variant not taking texts is "make_polygon_layer". A Variant only taking * texts is "make_text_layer". */ - db::Region *make_layer (unsigned int layer_index); + db::Region *make_layer (unsigned int layer_index, const std::string &name = std::string ()); /** * @brief Creates a new region representing an original layer taking texts only * See "make_layer" for details. */ - db::Region *make_text_layer (unsigned int layer_index); + db::Region *make_text_layer (unsigned int layer_index, const std::string &name = std::string ()); /** * @brief Creates a new region representing an original layer taking polygons and texts * See "make_layer" for details. */ - db::Region *make_polygon_layer (unsigned int layer_index); + db::Region *make_polygon_layer (unsigned int layer_index, const std::string &name = std::string ()); /** * @brief Extracts devices diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 0c824e1f4..b77bd8f1c 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -52,8 +52,8 @@ namespace db * connect( ...) - connects layer1 with the following layers [short key: C] * global( ...) - connects a layer with the given global nets [short key: G] * circuit( [circuit-def]) - circuit (cell) [short key: X] - * device( [device-footprint-def]) - * - device footprint [short key: D] + * device( [device-abstract-def]) + * - device abstract [short key: D] * * [circuit-def]: * @@ -72,7 +72,7 @@ namespace db * rect( ) * - defines a rectangle [short key: R] * - * [device-footprint-def]: + * [device-abstract-def]: * * terminal( [geometry-def]) * - specifies the terminal geometry [short key: empty] @@ -80,7 +80,7 @@ namespace db * [device-def]: * * param( ) - defines a parameter [short key P] - * footprint() - links to a geometrical device footprint on top level [short key F] + * abstract() - links to a geometrical device abstract on top level [short key A] * terminal( ) * - specifies connection of the terminal with * a net (short key: empty) @@ -110,7 +110,7 @@ static std::string subcircuit_key ("subcircuit"); static std::string polygon_key ("polygon"); static std::string rect_key ("rect"); static std::string terminal_key ("terminal"); -static std::string footprint_key ("footprint"); +static std::string abstract_key ("abstract"); static std::string label_key ("label"); static std::string param_key ("param"); static std::string location_key ("location"); @@ -140,20 +140,31 @@ static std::string name_for_layer (const db::Layout *layout, unsigned int l) void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) { - const int version = 1; + bool any = false; + + const int version = 0; const db::Layout *ly = l2n->internal_layout (); const db::Netlist *nl = l2n->netlist (); - *mp_stream << "# General" << endl; - *mp_stream << version_key << "(" << version << ")" << endl; + *mp_stream << "# General section" << endl; + *mp_stream << "# Lists general definitions." << endl << endl; + if (version > 0) { + *mp_stream << version_key << "(" << version << ")" << endl; + } *mp_stream << top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; *mp_stream << unit_key << "(" << ly->dbu () << ")" << endl; - *mp_stream << endl << "# Layers" << endl; - for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { + *mp_stream << endl << "# Layer section" << endl; + *mp_stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl; + *mp_stream << endl << "# Mask layers" << endl; + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { *mp_stream << layer_key << "(" << name_for_layer (ly, *l) << ")" << endl; + } + + *mp_stream << endl << "# Mask layer connectivity" << endl; + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l); db::Connectivity::layer_iterator cb = l2n->connectivity ().begin_connected (*l); @@ -165,9 +176,18 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) *mp_stream << ")" << endl; } + } + + any = false; + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { + db::Connectivity::global_nets_iterator ge = l2n->connectivity ().end_global_connections (*l); db::Connectivity::global_nets_iterator gb = l2n->connectivity ().begin_global_connections (*l); if (gb != ge) { + if (! any) { + *mp_stream << endl << "# Global nets and connectivity" << endl; + any = true; + } *mp_stream << global_key << "(" << name_for_layer (ly, *l); for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) { *mp_stream << " " << tl::to_word_or_quoted_string (l2n->connectivity ().global_net_name (*g)); @@ -177,7 +197,10 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) } - *mp_stream << endl << "# Device footprints" << endl; + if (nl->begin_device_models () != nl->end_device_models ()) { + *mp_stream << endl << "# Device abstracts section" << endl; + *mp_stream << "# Device abstracts list the pin shapes of the devices." << endl; + } for (db::Netlist::const_device_model_iterator m = nl->begin_device_models (); m != nl->end_device_models (); ++m) { if (m->device_class ()) { *mp_stream << device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl; @@ -186,6 +209,8 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) } } + *mp_stream << endl << "# Circuit section" << endl; + *mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl; for (db::Netlist::const_top_down_circuit_iterator i = nl->begin_top_down (); i != nl->end_top_down (); ++i) { const db::Circuit *x = *i; *mp_stream << endl << "# Circuit " << x->name () << endl; @@ -197,24 +222,38 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit) { - for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { - write (l2n, *n); - } - - for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { - const db::Net *net = circuit.net_for_pin (p->id ()); - if (net) { - *mp_stream << indent1 << pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl; + if (circuit.begin_nets () != circuit.end_nets ()) { + *mp_stream << endl << indent1 << "# Nets with their geometries" << endl; + for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + write (l2n, *n); } } - for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { - write (l2n, *d); + if (circuit.begin_pins () != circuit.end_pins ()) { + *mp_stream << endl << indent1 << "# Outgoing pins and their connections to nets" << endl; + for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { + const db::Net *net = circuit.net_for_pin (p->id ()); + if (net) { + *mp_stream << indent1 << pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl; + } + } } - for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) { - write (l2n, *x); + if (circuit.begin_devices () != circuit.end_devices ()) { + *mp_stream << endl << indent1 << "# Devices and their connections" << endl; + for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { + write (l2n, *d); + } } + + if (circuit.begin_subcircuits () != circuit.end_subcircuits ()) { + *mp_stream << endl << indent1 << "# Subcircuits and their connections" << endl; + for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) { + write (l2n, *x); + } + } + + *mp_stream << endl; } template @@ -226,12 +265,14 @@ void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr) } } -void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const std::string &lname) +void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname) { + db::ICplxTrans t = tr * db::ICplxTrans (s->trans ()); + const db::Polygon &poly = s->obj (); if (poly.is_box ()) { - db::Box box = s->trans () * poly.box (); + db::Box box = t * poly.box (); *mp_stream << rect_key << "(" << lname; *mp_stream << " " << box.left () << " " << box.bottom (); *mp_stream << " " << box.right () << " " << box.top (); @@ -242,9 +283,9 @@ void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const std::s *mp_stream << polygon_key << "(" << lname; if (poly.holes () > 0) { db::SimplePolygon sp (poly); - write_points (*mp_stream, sp, s->trans ()); + write_points (*mp_stream, sp, t); } else { - write_points (*mp_stream, poly, s->trans ()); + write_points (*mp_stream, poly, t); } *mp_stream << ")"; @@ -262,18 +303,36 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { - const db::local_cluster &lc = clusters.clusters_per_cell (circuit->cell_index ()).cluster_by_id (net.cluster_id ()); - for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { + db::cell_index_type cci = circuit->cell_index (); + db::cell_index_type prev_ci = cci; + + for (db::recursive_cluster_shape_iterator si (clusters, *l, cci, net.cluster_id ()); ! si.at_end (); ) { + + // NOTE: we don't recursive into circuits which will later be output. However, as circuits may + // vanish in "purge" but the clusters will still be there we need to recursive into clusters from + // unknown cells. + db::cell_index_type ci = si.cell_index (); + if (ci != prev_ci && ci != cci && l2n->netlist ()->circuit_by_cell_index (ci)) { + + si.skip_cell (); + + } else { + + if (! any) { + *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; + any = true; + } + + *mp_stream << indent2; + write (si.operator-> (), si.trans (), name_for_layer (ly, *l)); + *mp_stream << endl; + + prev_ci = ci; + + ++si; - if (! any) { - *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; - any = true; } - *mp_stream << indent2; - write (s.operator-> (), name_for_layer (ly, *l)); - *mp_stream << endl; - } } @@ -341,21 +400,25 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const const db::hier_clusters &clusters = l2n->net_clusters (); const db::Connectivity &conn = l2n->connectivity (); - for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { - for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + *mp_stream << indent1 << terminal_key << "(" << t->name () << endl; + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { const db::local_cluster &lc = clusters.clusters_per_cell (device_model.cell_index ()).cluster_by_id (device_model.cluster_id_for_terminal (t->id ())); for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { - *mp_stream << indent1; - write (s.operator-> (), name_for_layer (ly, *l)); + *mp_stream << indent2; + write (s.operator-> (), db::ICplxTrans (), name_for_layer (ly, *l)); *mp_stream << endl; } } + *mp_stream << indent1 << ")" << endl; + } } @@ -370,7 +433,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const *mp_stream << indent2 << location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl; if (device.device_model ()) { - *mp_stream << indent2 << footprint_key << "(" << tl::to_word_or_quoted_string (device.device_model ()->name ()) << ")" << endl; + *mp_stream << indent2 << abstract_key << "(" << tl::to_word_or_quoted_string (device.device_model ()->name ()) << ")" << endl; } const std::vector &pd = device.device_class ()->parameter_definitions (); diff --git a/src/db/db/dbLayoutToNetlistWriter.h b/src/db/db/dbLayoutToNetlistWriter.h index 2955ba66b..47ad24102 100644 --- a/src/db/db/dbLayoutToNetlistWriter.h +++ b/src/db/db/dbLayoutToNetlistWriter.h @@ -68,7 +68,7 @@ private: void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit); void write (const db::LayoutToNetlist *l2n, const db::Device &device); void write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model); - void write (const db::PolygonRef *s, const std::string &lname); + void write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname); }; } diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 3974dddc5..3ba88b8c7 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -95,21 +95,34 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("max_vertex_count", &db::LayoutToNetlist::max_vertex_count, "See \\max_vertex_count= for details about this attribute." ) + - gsi::method ("make_layer", &db::LayoutToNetlist::make_layer, gsi::arg ("layer_index"), - "@brief Creates a new region representing an original layer\n" - "'layer_index'' is the layer index of the desired layer in the original layout.\n" - "The Region object returned is a new object and must be deleted by the caller.\n" + gsi::method ("name", &db::LayoutToNetlist::name, gsi::arg ("l"), + "@brief Names the given layer\n" + "'l' must be a hierarchical region derived with \\make_layer, \\make_text_layer or \\make_polygon_layer or " + "a region derived from those by boolean operations or other hierarchical operations.\n" + "\n" + "Naming a layer allows the system to indicate the layer in various contexts, i.e. " + "when writing the data to a file.\n" + ) + + gsi::method ("make_layer", &db::LayoutToNetlist::make_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), + "@brief Creates a new hierarchical region representing an original layer\n" + "'layer_index' is the layer index of the desired layer in the original layout.\n" "This variant produces polygons and takes texts for net name annotation.\n" "A variant not taking texts is \\make_polygon_layer. A Variant only taking\n" - "texts is \\make_text_layer.\n""" + "texts is \\make_text_layer.\n" + "\n" + "The name is optional. If given, the layer will already be named accordingly (see \\name).\n" ) + - gsi::method ("make_text_layer", &db::LayoutToNetlist::make_text_layer, gsi::arg ("layer_index"), + gsi::method ("make_text_layer", &db::LayoutToNetlist::make_text_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), "@brief Creates a new region representing an original layer taking texts only\n" "See \\make_layer for details.\n" + "\n" + "The name is optional. If given, the layer will already be named accordingly (see \\name).\n" ) + - gsi::method ("make_polygon_layer", &db::LayoutToNetlist::make_polygon_layer, gsi::arg ("layer_index"), + gsi::method ("make_polygon_layer", &db::LayoutToNetlist::make_polygon_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), "@brief Creates a new region representing an original layer taking polygons and texts\n" "See \\make_layer for details.\n" + "\n" + "The name is optional. If given, the layer will already be named accordingly (see \\name).\n" ) + gsi::method ("extract_devices", &db::LayoutToNetlist::extract_devices, gsi::arg ("extractor"), gsi::arg ("layers"), "@brief Extracts devices\n" diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 337780a04..c81cc404b 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -702,10 +702,14 @@ static std::string path2string (const db::Layout &ly, db::cell_index_type ci, co return res; } -static std::string rcsiter2string (const db::Layout &ly, db::cell_index_type ci, db::recursive_cluster_shape_iterator si) +static std::string rcsiter2string (const db::Layout &ly, db::cell_index_type ci, db::recursive_cluster_shape_iterator si, db::cell_index_type ci2skip = std::numeric_limits::max ()) { std::string res; while (! si.at_end ()) { + if (si.cell_index () == ci2skip) { + si.skip_cell (); + continue; + } db::Polygon poly = si->obj (); poly.transform (si->trans ()); poly.transform (si.trans ()); @@ -766,6 +770,16 @@ TEST(41_HierClustersRecursiveClusterShapeIterator) } EXPECT_EQ (n, 1); EXPECT_EQ (res, "TOP:(0,0;0,1000;1000,1000;1000,0);TOP/C1:(0,10;0,510;2000,510;2000,10);TOP/C2:(0,30;0,2030;500,2030;500,30);TOP/C2/C1:(0,50;0,550;2000,550;2000,50)"); + + res.clear (); + n = 0; + cluster = &hc.clusters_per_cell (top.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + res = rcsiter2string (ly, top.cell_index (), db::recursive_cluster_shape_iterator (hc, l1, top.cell_index (), i->id ()), c1.cell_index ()); + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (res, "TOP:(0,0;0,1000;1000,1000;1000,0);TOP/C2:(0,30;0,2030;500,2030;500,30)"); } TEST(41_HierClustersRecursiveClusterIterator) diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 0213af5dd..4484d10a6 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -72,27 +72,33 @@ TEST(1_WriterBasic) db::Cell &tc = ly.cell (*ly.begin_top_down ()); db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); - std::auto_ptr rnwell (l2n.make_layer (nwell)); - std::auto_ptr ractive (l2n.make_layer (active)); - std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); - std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); - std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); - std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); - std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); - std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); - std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); - std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); - std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); // derived regions db::Region rpactive = *ractive & *rnwell; db::Region rpgate = rpactive & *rpoly; db::Region rpsd = rpactive - rpgate; + l2n.name (rpactive, "pactive"); + l2n.name (rpgate, "pgate"); + l2n.name (rpsd, "psd"); db::Region rnactive = *ractive - *rnwell; db::Region rngate = rnactive & *rpoly; db::Region rnsd = rnactive - rngate; + l2n.name (rnactive, "nactive"); + l2n.name (rngate, "ngate"); + l2n.name (rnsd, "nsd"); db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); @@ -157,18 +163,23 @@ TEST(1_WriterBasic) rpoly_lbl.reset (0); l2n.extract_netlist (); + l2n.netlist ()->purge (); - tl::OutputMemoryStream mem; + std::string path = tmp_file ("tmp_l2nwriter_1.txt"); { - tl::OutputStream stream (mem); + tl::OutputStream stream (path); db::LayoutToNetlistStandardWriter writer (stream); writer.write (&l2n); } -// TODO: too big for inlined text ... -#if 0 - EXPECT_EQ (std::string (mem.data (), mem.size ()), - "" - ); -#endif + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } } diff --git a/testdata/algo/l2n_writer_au.txt b/testdata/algo/l2n_writer_au.txt new file mode 100644 index 000000000..c8f789c19 --- /dev/null +++ b/testdata/algo/l2n_writer_au.txt @@ -0,0 +1,477 @@ +# General section +# Lists general definitions. + +top(RINGO) +unit(0.001) + +# Layer section +# This section lists the mask layers (drawing or derived) and their connections. + +# Mask layers +layer(poly) +layer(poly_lbl) +layer(diff_cont) +layer(poly_cont) +layer(metal1) +layer(metal1_lbl) +layer(via1) +layer(metal2) +layer(metal2_lbl) +layer(psd) +layer(nsd) + +# Mask layer connectivity +connect(poly poly poly_lbl poly_cont) +connect(poly_lbl poly) +connect(diff_cont diff_cont metal1 psd nsd) +connect(poly_cont poly poly_cont metal1) +connect(metal1 diff_cont poly_cont metal1 metal1_lbl via1) +connect(metal1_lbl metal1) +connect(via1 metal1 via1 metal2) +connect(metal2 via1 metal2 metal2_lbl) +connect(metal2_lbl metal2) +connect(psd diff_cont psd) +connect(nsd diff_cont nsd) + +# Device abstracts section +# Device abstracts list the pin shapes of the devices. +device(D$PMOS PMOS + terminal(S + rect(poly -125 -475 125 475) + ) + terminal(G + rect(psd -650 -475 -125 475) + ) + terminal(D + rect(psd 125 -475 675 475) + ) +) +device(D$PMOS$1 PMOS + terminal(S + rect(poly -125 -475 125 475) + ) + terminal(G + rect(psd -675 -475 -125 475) + ) + terminal(D + rect(psd 125 -475 650 475) + ) +) +device(D$NMOS NMOS + terminal(S + rect(poly -125 -475 125 475) + ) + terminal(G + rect(nsd -650 -475 -125 475) + ) + terminal(D + rect(nsd 125 -475 675 475) + ) +) +device(D$NMOS$1 NMOS + terminal(S + rect(poly -125 -475 125 475) + ) + terminal(G + rect(nsd -675 -475 -125 475) + ) + terminal(D + rect(nsd 125 -475 650 475) + ) +) + +# Circuit section +# Circuits are the hierarchical building blocks of the netlist. + +# Circuit RINGO +circuit(RINGO + + # Nets with their geometries + net(FB + rect(diff_cont 22850 2490 23070 2710) + rect(diff_cont 22850 2890 23070 3110) + rect(diff_cont 22850 -310 23070 -90) + rect(diff_cont 22850 90 23070 310) + rect(metal1 -1700 1620 -1340 1980) + rect(via1 -1645 1675 -1395 1925) + rect(via1 22835 1675 23085 1925) + rect(metal2 -1720 1600 23160 2000) + rect(metal2_lbl -1 1799 1 1801) + ) + net(VSS + rect(diff_cont 2530 -310 2750 -90) + rect(diff_cont 2530 90 2750 310) + rect(diff_cont 2530 90 2750 310) + rect(diff_cont 2530 -310 2750 -90) + rect(diff_cont -110 -310 110 -90) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 -310 110 -90) + rect(diff_cont 5170 -310 5390 -90) + rect(diff_cont 5170 90 5390 310) + rect(diff_cont 5170 90 5390 310) + rect(diff_cont 5170 -310 5390 -90) + rect(diff_cont 7810 -310 8030 -90) + rect(diff_cont 7810 90 8030 310) + rect(diff_cont 7810 90 8030 310) + rect(diff_cont 7810 -310 8030 -90) + rect(diff_cont 10450 -310 10670 -90) + rect(diff_cont 10450 90 10670 310) + rect(diff_cont 10450 90 10670 310) + rect(diff_cont 10450 -310 10670 -90) + rect(diff_cont 13090 -310 13310 -90) + rect(diff_cont 13090 90 13310 310) + rect(diff_cont 13090 90 13310 310) + rect(diff_cont 13090 -310 13310 -90) + rect(diff_cont 15730 -310 15950 -90) + rect(diff_cont 15730 90 15950 310) + rect(diff_cont 15730 90 15950 310) + rect(diff_cont 15730 -310 15950 -90) + rect(diff_cont 18370 -310 18590 -90) + rect(diff_cont 18370 90 18590 310) + rect(diff_cont 18370 90 18590 310) + rect(diff_cont 18370 -310 18590 -90) + rect(diff_cont 21010 -310 21230 -90) + rect(diff_cont 21010 90 21230 310) + rect(diff_cont 21010 90 21230 310) + rect(diff_cont 21010 -310 21230 -90) + rect(diff_cont 23650 -310 23870 -90) + rect(diff_cont 23650 90 23870 310) + rect(diff_cont 23650 90 23870 310) + rect(diff_cont 23650 -310 23870 -90) + rect(metal1 2460 -380 2820 380) + rect(metal1 2460 -380 2820 380) + rect(metal1 -180 -380 180 380) + rect(metal1 -180 -380 180 380) + rect(metal1 5100 -380 5460 380) + rect(metal1 5100 -380 5460 380) + rect(metal1 7740 -380 8100 380) + rect(metal1 7740 -380 8100 380) + rect(metal1 10380 -380 10740 380) + rect(metal1 10380 -380 10740 380) + rect(metal1 13020 -380 13380 380) + rect(metal1 13020 -380 13380 380) + rect(metal1 15660 -380 16020 380) + rect(metal1 15660 -380 16020 380) + rect(metal1 18300 -380 18660 380) + rect(metal1 18300 -380 18660 380) + rect(metal1 20940 -380 21300 380) + rect(metal1 20940 -380 21300 380) + rect(metal1 23580 -380 23940 380) + rect(metal1 23580 -380 23940 380) + rect(metal2_lbl -1 -1 1 1) + ) + net(VDD + rect(diff_cont 2530 2490 2750 2710) + rect(diff_cont 2530 2890 2750 3110) + rect(diff_cont 2530 2890 2750 3110) + rect(diff_cont 2530 2490 2750 2710) + rect(diff_cont -110 2490 110 2710) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2490 110 2710) + rect(diff_cont 5170 2490 5390 2710) + rect(diff_cont 5170 2890 5390 3110) + rect(diff_cont 5170 2890 5390 3110) + rect(diff_cont 5170 2490 5390 2710) + rect(diff_cont 7810 2490 8030 2710) + rect(diff_cont 7810 2890 8030 3110) + rect(diff_cont 7810 2890 8030 3110) + rect(diff_cont 7810 2490 8030 2710) + rect(diff_cont 10450 2490 10670 2710) + rect(diff_cont 10450 2890 10670 3110) + rect(diff_cont 10450 2890 10670 3110) + rect(diff_cont 10450 2490 10670 2710) + rect(diff_cont 13090 2490 13310 2710) + rect(diff_cont 13090 2890 13310 3110) + rect(diff_cont 13090 2890 13310 3110) + rect(diff_cont 13090 2490 13310 2710) + rect(diff_cont 15730 2490 15950 2710) + rect(diff_cont 15730 2890 15950 3110) + rect(diff_cont 15730 2890 15950 3110) + rect(diff_cont 15730 2490 15950 2710) + rect(diff_cont 18370 2490 18590 2710) + rect(diff_cont 18370 2890 18590 3110) + rect(diff_cont 18370 2890 18590 3110) + rect(diff_cont 18370 2490 18590 2710) + rect(diff_cont 21010 2490 21230 2710) + rect(diff_cont 21010 2890 21230 3110) + rect(diff_cont 21010 2890 21230 3110) + rect(diff_cont 21010 2490 21230 2710) + rect(diff_cont 23650 2490 23870 2710) + rect(diff_cont 23650 2890 23870 3110) + rect(diff_cont 23650 2890 23870 3110) + rect(diff_cont 23650 2490 23870 2710) + rect(metal1 2460 2420 2820 3180) + rect(metal1 2460 2420 2820 3180) + rect(metal1 -180 2420 180 3180) + rect(metal1 -180 2420 180 3180) + rect(metal1 5100 2420 5460 3180) + rect(metal1 5100 2420 5460 3180) + rect(metal1 7740 2420 8100 3180) + rect(metal1 7740 2420 8100 3180) + rect(metal1 10380 2420 10740 3180) + rect(metal1 10380 2420 10740 3180) + rect(metal1 13020 2420 13380 3180) + rect(metal1 13020 2420 13380 3180) + rect(metal1 15660 2420 16020 3180) + rect(metal1 15660 2420 16020 3180) + rect(metal1 18300 2420 18660 3180) + rect(metal1 18300 2420 18660 3180) + rect(metal1 20940 2420 21300 3180) + rect(metal1 20940 2420 21300 3180) + rect(metal1 23580 2420 23940 3180) + rect(metal1 23580 2420 23940 3180) + rect(metal2_lbl -1 2799 1 2801) + ) + net($I19 + rect(diff_cont 690 2890 910 3110) + rect(diff_cont 690 2490 910 2710) + rect(diff_cont 690 90 910 310) + rect(diff_cont 690 -310 910 -90) + ) + net($I8 + rect(diff_cont 21810 2890 22030 3110) + rect(diff_cont 21810 2490 22030 2710) + rect(diff_cont 21810 90 22030 310) + rect(diff_cont 21810 -310 22030 -90) + ) + net($I7 + rect(diff_cont 19170 2890 19390 3110) + rect(diff_cont 19170 2490 19390 2710) + rect(diff_cont 19170 90 19390 310) + rect(diff_cont 19170 -310 19390 -90) + ) + net($I6 + rect(diff_cont 16530 2890 16750 3110) + rect(diff_cont 16530 2490 16750 2710) + rect(diff_cont 16530 90 16750 310) + rect(diff_cont 16530 -310 16750 -90) + ) + net($I5 + rect(diff_cont 13890 2890 14110 3110) + rect(diff_cont 13890 2490 14110 2710) + rect(diff_cont 13890 90 14110 310) + rect(diff_cont 13890 -310 14110 -90) + ) + net($I4 + rect(diff_cont 11250 2890 11470 3110) + rect(diff_cont 11250 2490 11470 2710) + rect(diff_cont 11250 90 11470 310) + rect(diff_cont 11250 -310 11470 -90) + ) + net($I3 + rect(diff_cont 8610 2890 8830 3110) + rect(diff_cont 8610 2490 8830 2710) + rect(diff_cont 8610 90 8830 310) + rect(diff_cont 8610 -310 8830 -90) + ) + net($I2 + rect(diff_cont 5970 2890 6190 3110) + rect(diff_cont 5970 2490 6190 2710) + rect(diff_cont 5970 90 6190 310) + rect(diff_cont 5970 -310 6190 -90) + ) + net($I1 + rect(diff_cont 3330 2890 3550 3110) + rect(diff_cont 3330 2490 3550 2710) + rect(diff_cont 3330 90 3550 310) + rect(diff_cont 3330 -310 3550 -90) + ) + + # Subcircuits and their connections + subcircuit($1 location(23760 0) + pin(IN $I8) + pin($1 FB) + pin($3 VSS) + pin($4 VDD) + ) + subcircuit($2 location(0 0) + pin(IN FB) + pin(OUT $I19) + pin($3 VSS) + pin($4 VDD) + ) + subcircuit($3 location(2640 0) + pin(IN $I19) + pin(OUT $I1) + pin($3 VSS) + pin($4 VDD) + ) + subcircuit($4 location(5280 0) + pin(IN $I1) + pin(OUT $I2) + pin($3 VSS) + pin($4 VDD) + ) + subcircuit($5 location(7920 0) + pin(IN $I2) + pin(OUT $I3) + pin($3 VSS) + pin($4 VDD) + ) + subcircuit($6 location(10560 0) + pin(IN $I3) + pin(OUT $I4) + pin($3 VSS) + pin($4 VDD) + ) + subcircuit($7 location(13200 0) + pin(IN $I4) + pin(OUT $I5) + pin($3 VSS) + pin($4 VDD) + ) + subcircuit($8 location(15840 0) + pin(IN $I5) + pin(OUT $I6) + pin($3 VSS) + pin($4 VDD) + ) + subcircuit($9 location(18480 0) + pin(IN $I6) + pin(OUT $I7) + pin($3 VSS) + pin($4 VDD) + ) + subcircuit($10 location(21120 0) + pin(IN $I7) + pin(OUT $I8) + pin($3 VSS) + pin($4 VDD) + ) + +) + +# Circuit INV2 +circuit(INV2 + + # Nets with their geometries + net(IN + rect(poly -525 -250 -275 2250) + rect(poly -1700 1620 -400 1980) + rect(poly -525 -800 -275 800) + rect(poly -525 -475 -275 475) + rect(poly -525 2000 -275 3600) + rect(poly -525 2325 -275 3275) + rect(poly_lbl -801 1799 -799 1801) + rect(poly_cont -1630 1690 -1410 1910) + ) + net($2 + rect(poly 275 -250 525 2250) + rect(poly 220 820 580 1180) + rect(poly 275 2000 525 3600) + rect(poly 275 2325 525 3275) + rect(poly 275 -800 525 800) + rect(poly 275 -475 525 475) + rect(diff_cont -910 2490 -690 2710) + rect(diff_cont -910 2890 -690 3110) + rect(diff_cont -910 -310 -690 -90) + rect(diff_cont -910 90 -690 310) + rect(poly_cont 290 890 510 1110) + rect(metal1 -800 820 580 1180) + rect(metal1 -980 -420 -620 2420) + rect(metal1 -980 2420 -620 3180) + rect(metal1 -980 -380 -620 380) + rect(psd -1050 2325 -525 3275) + rect(psd -1050 2325 -525 3275) + rect(nsd -1050 -475 -525 475) + rect(nsd -1050 -475 -525 475) + ) + net(OUT + rect(diff_cont 690 2890 910 3110) + rect(diff_cont 690 2490 910 2710) + rect(diff_cont 690 90 910 310) + rect(diff_cont 690 -310 910 -90) + polygon(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20) + rect(metal1 620 2420 980 3180) + rect(metal1 620 -380 980 380) + rect(metal1_lbl 799 1799 801 1801) + rect(psd 525 2325 1050 3275) + rect(psd 525 2325 1050 3275) + rect(nsd 525 -475 1050 475) + rect(nsd 525 -475 1050 475) + ) + net($4 + rect(diff_cont -110 -310 110 -90) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 -310 110 -90) + rect(metal1 -180 -380 180 380) + rect(metal1 -180 -380 180 380) + rect(via1 -125 -325 125 -75) + rect(via1 -125 75 125 325) + rect(metal2 -1400 -450 1400 450) + rect(nsd -275 -475 275 475) + rect(nsd -275 -475 275 475) + rect(nsd -275 -475 275 475) + ) + net($5 + rect(diff_cont -110 2490 110 2710) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2490 110 2710) + rect(metal1 -180 2420 180 3180) + rect(metal1 -180 2420 180 3180) + rect(via1 -125 2475 125 2725) + rect(via1 -125 2875 125 3125) + rect(metal2 -1400 2350 1400 3250) + rect(psd -275 2325 275 3275) + rect(psd -275 2325 275 3275) + rect(psd -275 2325 275 3275) + ) + + # Outgoing pins and their connections to nets + pin(IN IN) + pin($1 $2) + pin(OUT OUT) + pin($3 $4) + pin($4 $5) + + # Devices and their connections + device($1 PMOS + location(-400 2800) + abstract(D$PMOS) + param(L 0.25) + param(W 0.95) + param(AS 0.49875) + param(AD 0.26125) + terminal(S $2) + terminal(G IN) + terminal(D $5) + ) + device($2 PMOS + location(400 2800) + abstract(D$PMOS$1) + param(L 0.25) + param(W 0.95) + param(AS 0.26125) + param(AD 0.49875) + terminal(S $5) + terminal(G $2) + terminal(D OUT) + ) + device($3 NMOS + location(-400 0) + abstract(D$NMOS) + param(L 0.25) + param(W 0.95) + param(AS 0.49875) + param(AD 0.26125) + terminal(S $2) + terminal(G IN) + terminal(D $4) + ) + device($4 NMOS + location(400 0) + abstract(D$NMOS$1) + param(L 0.25) + param(W 0.95) + param(AS 0.26125) + param(AD 0.49875) + terminal(S $4) + terminal(G $2) + terminal(D OUT) + ) + +) From 8213e71a792f4c1c376bf78a8ab41b6e6de8f126 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Jan 2019 22:19:08 +0100 Subject: [PATCH 200/335] WIP: l2n reader implementation, some bug fixes, refactoring. --- src/db/db/db.pro | 6 +- src/db/db/dbHierNetworkProcessor.cc | 5 - src/db/db/dbLayoutToNetlist.cc | 33 +- src/db/db/dbLayoutToNetlist.h | 28 + src/db/db/dbLayoutToNetlistFormatDefs.cc | 73 ++ src/db/db/dbLayoutToNetlistFormatDefs.h | 118 +++ src/db/db/dbLayoutToNetlistReader.cc | 688 ++++++++++++++++++ src/db/db/dbLayoutToNetlistReader.h | 75 +- src/db/db/dbLayoutToNetlistWriter.cc | 222 +++--- src/db/db/dbLayoutToNetlistWriter.h | 16 +- src/db/db/dbNetlistDeviceExtractor.cc | 10 +- src/db/db/dbNetlistExtractor.cc | 34 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 6 +- .../dbLayoutToNetlistReaderTests.cc | 66 ++ .../dbLayoutToNetlistWriterTests.cc | 2 +- src/db/unit_tests/unit_tests.pro | 3 +- testdata/algo/l2n_writer_au.txt | 298 ++++---- 17 files changed, 1364 insertions(+), 319 deletions(-) create mode 100644 src/db/db/dbLayoutToNetlistFormatDefs.cc create mode 100644 src/db/db/dbLayoutToNetlistFormatDefs.h create mode 100644 src/db/unit_tests/dbLayoutToNetlistReaderTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 8401e0aef..8ad80e277 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -161,7 +161,8 @@ SOURCES = \ dbPin.cc \ dbLayoutToNetlistReader.cc \ dbLayoutToNetlistWriter.cc \ - dbDeviceModel.cc + dbDeviceModel.cc \ + dbLayoutToNetlistFormatDefs.cc HEADERS = \ dbArray.h \ @@ -287,7 +288,8 @@ HEADERS = \ dbSubCircuit.h \ dbLayoutToNetlistReader.h \ dbLayoutToNetlistWriter.h \ - dbDeviceModel.h + dbDeviceModel.h \ + dbLayoutToNetlistFormatDefs.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index fb2ea01f1..8ad42cd7d 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1664,11 +1664,6 @@ template void hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) { - if (m_per_cell_clusters.find (cell.cell_index ()) != m_per_cell_clusters.end ()) { - // skip pre-build clusters (from devices) - return; - } - std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); if (tl::verbosity () >= 40) { tl::log << msg; diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 8d8a89381..81127318e 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -78,6 +78,18 @@ size_t LayoutToNetlist::max_vertex_count () const return m_dss.max_vertex_count (); } +db::Region *LayoutToNetlist::make_layer (const std::string &n) +{ + db::RecursiveShapeIterator si (m_iter); + si.shape_flags (db::ShapeIterator::Nothing); + + db::Region *region = new db::Region (si, m_dss); + if (! n.empty ()) { + name (*region, n); + } + return region; +} + db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::string &n) { db::RecursiveShapeIterator si (m_iter); @@ -199,8 +211,6 @@ void LayoutToNetlist::extract_netlist () mp_netlist.reset (new db::Netlist ()); } - m_net_clusters.clear (); - db::NetlistExtractor netex; netex.extract_nets(m_dss, m_conn, *mp_netlist, m_net_clusters); @@ -217,6 +227,16 @@ const db::Cell *LayoutToNetlist::internal_top_cell () const return &m_dss.const_initial_cell (); } +db::Layout *LayoutToNetlist::internal_layout () +{ + return &m_dss.layout (); +} + +db::Cell *LayoutToNetlist::internal_top_cell () +{ + return &m_dss.initial_cell (); +} + void LayoutToNetlist::name (const db::Region ®ion, const std::string &name) { unsigned int li = layer_of (region); @@ -269,6 +289,15 @@ db::Netlist *LayoutToNetlist::netlist () const return mp_netlist.get (); } +db::Netlist *LayoutToNetlist::make_netlist () +{ + if (! mp_netlist.get ()) { + mp_netlist.reset (new db::Netlist ()); + } + return mp_netlist.get (); +} + + template static void deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr) { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index df01397c1..7a0be2655 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -133,6 +133,11 @@ public: return internal_layout ()->get_properties (layer_of (region)).name; } + /** + * @brief Creates a new empty region + */ + db::Region *make_layer (const std::string &n); + /** * @brief Creates a new region representing an original layer * "layer_index" is the layer index of the desired layer in the original layout. @@ -216,6 +221,16 @@ public: */ const db::Cell *internal_top_cell () const; + /** + * @brief Gets the internal layout (non-const version) + */ + db::Layout *internal_layout (); + + /** + * @brief Gets the internal top cell (non-const version) + */ + db::Cell *internal_top_cell (); + /** * @brief Gets the connectivity object */ @@ -251,6 +266,11 @@ public: */ db::Netlist *netlist () const; + /** + * @brief gets the netlist extracted or make on if none exists yet. + */ + db::Netlist *make_netlist (); + /** * @brief Gets the hierarchical shape clusters derived in the net extraction. * NOTE: the layer and cell indexes used inside this structure refer to the @@ -261,6 +281,14 @@ public: return m_net_clusters; } + /** + * @brief Gets the hierarchical shape clusters derived in the net extraction (non-conver version) + */ + db::hier_clusters &net_clusters () + { + return m_net_clusters; + } + /** * @brief Returns all shapes of a specific net and layer. * diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.cc b/src/db/db/dbLayoutToNetlistFormatDefs.cc new file mode 100644 index 000000000..572fdccb8 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistFormatDefs.cc @@ -0,0 +1,73 @@ + +/* + + 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 "dbLayoutToNetlistFormatDefs.h" + +namespace db +{ + +namespace l2n_std_format +{ + template<> DB_PUBLIC const std::string keys::version_key ("version"); + template<> DB_PUBLIC const std::string keys::description_key ("description"); + template<> DB_PUBLIC const std::string keys::top_key ("top"); + template<> DB_PUBLIC const std::string keys::unit_key ("unit"); + template<> DB_PUBLIC const std::string keys::layer_key ("layer"); + template<> DB_PUBLIC const std::string keys::connect_key ("connect"); + template<> DB_PUBLIC const std::string keys::global_key ("global"); + template<> DB_PUBLIC const std::string keys::circuit_key ("circuit"); + template<> DB_PUBLIC const std::string keys::net_key ("net"); + template<> DB_PUBLIC const std::string keys::device_key ("device"); + template<> DB_PUBLIC const std::string keys::polygon_key ("polygon"); + template<> DB_PUBLIC const std::string keys::rect_key ("rect"); + template<> DB_PUBLIC const std::string keys::terminal_key ("terminal"); + template<> DB_PUBLIC const std::string keys::abstract_key ("abstract"); + template<> DB_PUBLIC const std::string keys::param_key ("param"); + template<> DB_PUBLIC const std::string keys::location_key ("location"); + template<> DB_PUBLIC const std::string keys::rotation_key ("rotation"); + template<> DB_PUBLIC const std::string keys::mirror_key ("mirror"); + template<> DB_PUBLIC const std::string keys::scale_key ("scale"); + template<> DB_PUBLIC const std::string keys::pin_key ("pin"); + + template<> DB_PUBLIC const std::string keys::version_key ("V"); + template<> DB_PUBLIC const std::string keys::description_key ("B"); + template<> DB_PUBLIC const std::string keys::top_key ("W"); + template<> DB_PUBLIC const std::string keys::unit_key ("U"); + template<> DB_PUBLIC const std::string keys::layer_key ("L"); + template<> DB_PUBLIC const std::string keys::connect_key ("C"); + template<> DB_PUBLIC const std::string keys::global_key ("G"); + template<> DB_PUBLIC const std::string keys::circuit_key ("X"); + template<> DB_PUBLIC const std::string keys::net_key ("N"); + template<> DB_PUBLIC const std::string keys::device_key ("D"); + template<> DB_PUBLIC const std::string keys::polygon_key ("Q"); + template<> DB_PUBLIC const std::string keys::rect_key ("R"); + template<> DB_PUBLIC const std::string keys::terminal_key ("T"); + template<> DB_PUBLIC const std::string keys::abstract_key ("A"); + template<> DB_PUBLIC const std::string keys::param_key ("E"); + template<> DB_PUBLIC const std::string keys::location_key ("Y"); + template<> DB_PUBLIC const std::string keys::rotation_key ("O"); + template<> DB_PUBLIC const std::string keys::mirror_key ("M"); + template<> DB_PUBLIC const std::string keys::scale_key ("S"); + template<> DB_PUBLIC const std::string keys::pin_key ("P"); +} + +} diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h new file mode 100644 index 000000000..8d0a6e894 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -0,0 +1,118 @@ + +/* + + 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_dbLayoutToNetlistFormatDefs +#define HDR_dbLayoutToNetlistFormatDefs + +#include "dbCommon.h" + +#include + +namespace db +{ + +/** + * This is the internal persistency format for LayoutToNetlist + * + * It's intentionally *not* XML to keep the overhead low. + * + * Comments are introduced by hash: # ... + * Names are words (alphanumerical plus "$", "_", ".") or enclosed in single or double quotes. + * Escape character is backslash. + * Separator is either , or whitespace. Keywords and names are case sensitive. + * Short keys are provided for compacter representation. Short keys can be + * non-alpha (e.g. "*") or empty. + * Single-valued attributes can be given without brackets. + * All dimensions are in units of database unit. + * The file follows the declaration-before-use principle + * (circuits before subcircuits, nets before use ...) + * + * Global statements: + * + * version() - file format version [short key: V] + * description() - an arbitrary description text [short key: B] + * unit() - specifies the database unit [short key: U] + * top() - specifies the name of the top circuit [short key: W] + * layer() - define a layer [short key: L] + * connect( ...) - connects layer1 with the following layers [short key: C] + * global( ...) - connects a layer with the given global nets [short key: G] + * circuit( [circuit-def]) - circuit (cell) [short key: X] + * device( [device-abstract-def]) + * - device abstract [short key: D] + * + * [circuit-def]: + * + * net( [geometry-def]) - net geometry [short key: N] + * A net declaration shall be there also if no geometry + * is present + * pin( ) - outgoing pin connection [short key: P] + * device( [device-def]) + * - device with connections [short key: D] + * circuit( [circuit-def]) - subcircuit with connections [short key: X] + * + * [geometry-def]: + * + * polygon( ...) - defines a polygon [short key: Q] + * rect( ) + * - defines a rectangle [short key: R] + * + * [device-abstract-def]: + * + * terminal( [geometry-def]) + * - specifies the terminal geometry [short key: T] + * + * [device-def]: + * + * location( ) - location of the device [short key Y] + * must be before terminal + * param( ) - defines a parameter [short key E] + * terminal( ) + * - specifies connection of the terminal with + * a net (short key: T) + * + * [subcircuit-def]: + * + * location( ) - location of the subcircuit [short key Y] + * rotation() - rotation angle (in degree, default is 0) [short key O] + * mirror - if specified, the instance is mirrored before rotation [short key M] + * scale() - magnification (default is 1) [short key S] + * pin( ) - specifies connection of the pin with a net [short key: P] + */ + +namespace l2n_std_format +{ + template + struct DB_PUBLIC keys + { + static const std::string version_key, description_key, top_key, unit_key, + layer_key, connect_key, global_key, + circuit_key, net_key, device_key, subcircuit_key, + polygon_key, rect_key, terminal_key, abstract_key, + param_key, location_key, rotation_key, + mirror_key, scale_key, pin_key, + indent1, indent2; + }; +} + +} + +#endif diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index 192a6057d..fe3a3ac5b 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -21,10 +21,698 @@ */ #include "dbLayoutToNetlistReader.h" +#include "dbLayoutToNetlistFormatDefs.h" +#include "dbLayoutToNetlist.h" namespace db { +namespace l2n_std_reader { +class Layers +{ +public: + Layers () { } + + ~Layers () + { + for (std::map::const_iterator i = m_layers.begin (); i != m_layers.end (); ++i) { + delete i->second; + } + m_layers.clear (); + } + + void add (const std::string &name, db::Region *region) + { + if (m_layers.find (name) != m_layers.end ()) { + delete region; + throw tl::Exception (tl::to_string (tr ("Duplicate layer name: ")) + name); + } + m_layers.insert (std::make_pair (name, region)); + } + + db::Region &layer (const std::string &name) + { + std::map::const_iterator l = m_layers.find (name); + if (l == m_layers.end ()) { + throw tl::Exception (tl::to_string (tr ("Not a valid layer name: ")) + name); + } + return *l->second; + } + +private: + std::map m_layers; +}; + +class Brace +{ +public: + Brace (LayoutToNetlistStandardReader *reader) : mp_reader (reader), m_checked (false) + { + m_has_brace = reader->test ("("); + } + + operator bool () + { + if (! m_has_brace) { + m_checked = true; + return false; + } else if (mp_reader->test (")")) { + m_checked = true; + return false; + } else { + return true; + } + } + + void done () + { + if (m_has_brace && ! m_checked) { + mp_reader->expect (")"); + m_checked = true; + } + } + +private: + LayoutToNetlistStandardReader *mp_reader; + bool m_checked; + bool m_has_brace; +}; + +} + +typedef l2n_std_format::keys skeys; +typedef l2n_std_format::keys lkeys; + +LayoutToNetlistStandardReader::LayoutToNetlistStandardReader (tl::InputStream &stream) + : m_stream (stream), m_path (stream.absolute_path ()) +{ + skip (); +} + +bool +LayoutToNetlistStandardReader::test (const std::string &token) +{ + skip (); + return ! at_end () && m_ex.test (token.c_str ()); +} + +void +LayoutToNetlistStandardReader::expect (const std::string &token) +{ + m_ex.expect (token.c_str ()); +} + +void +LayoutToNetlistStandardReader::read_word_or_quoted (std::string &s) +{ + m_ex.read_word_or_quoted (s); +} + +int +LayoutToNetlistStandardReader::read_int () +{ + int i = 0; + m_ex.read (i); + return i; +} + +db::Coord +LayoutToNetlistStandardReader::read_coord () +{ + db::Coord i = 0; + m_ex.read (i); + return i; +} + +double +LayoutToNetlistStandardReader::read_double () +{ + double d = 0; + m_ex.read (d); + return d; +} + +bool +LayoutToNetlistStandardReader::at_end () +{ + return (m_ex.at_end () && m_stream.at_end ()); +} + +void +LayoutToNetlistStandardReader::skip () +{ + while (m_ex.at_end () || *m_ex.skip () == '#') { + if (m_stream.at_end ()) { + return; + } + m_line = m_stream.get_line (); + m_ex = tl::Extractor (m_line.c_str ()); + } +} + +void LayoutToNetlistStandardReader::read (db::LayoutToNetlist *l2n) +{ + try { + do_read (l2n); + } catch (tl::Exception &ex) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("%s in line: %d of %s")), ex.msg (), m_stream.line_number (), m_path)); + } +} + +void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) +{ + int version = 0; + std::string description; + + // TODO: there probably is a more efficient way to force the layout inside l2n to be made + l2n->make_layer (std::string ()); + tl_assert (l2n->internal_layout ()); + + l2n->make_netlist (); + + Layers layers; + + while (! at_end ()) { + + if (test (skeys::version_key) || test (lkeys::version_key)) { + + Brace br (this); + version = read_int (); + br.done (); + + } else if (test (skeys::description_key) || test (lkeys::description_key)) { + + Brace br (this); + read_word_or_quoted (description); + br.done (); + + } else if (test (skeys::unit_key) || test (lkeys::unit_key)) { + + Brace br (this); + double dbu = read_double (); + l2n->internal_layout ()->dbu (dbu); + br.done (); + + } else if (test (skeys::top_key) || test (lkeys::top_key)) { + + Brace br (this); + std::string top; + read_word_or_quoted (top); + l2n->internal_layout ()->rename_cell (l2n->internal_top_cell ()->cell_index (), top.c_str ()); + br.done (); + + } else if (test (skeys::layer_key) || test (lkeys::layer_key)) { + + Brace br (this); + std::string layer; + read_word_or_quoted (layer); + layers.add (layer, l2n->make_layer (layer)); + br.done (); + + } else if (test (skeys::connect_key) || test (lkeys::connect_key)) { + + Brace br (this); + std::string l1; + read_word_or_quoted (l1); + while (br) { + std::string l2; + read_word_or_quoted (l2); + l2n->connect (layers.layer (l1), layers.layer (l2)); + } + br.done (); + + } else if (test (skeys::global_key) || test (lkeys::global_key)) { + + Brace br (this); + std::string l1; + read_word_or_quoted (l1); + while (br) { + std::string g; + read_word_or_quoted (g); + l2n->connect_global (layers.layer (l1), g); + } + br.done (); + + } else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) { + + Brace br (this); + std::string name; + read_word_or_quoted (name); + + db::Circuit *circuit = new db::Circuit (); + circuit->set_name (name); + l2n->netlist ()->add_circuit (circuit); + + db::Layout *ly = l2n->internal_layout (); + db::cell_index_type ci = ly->add_cell (name.c_str ()); + circuit->set_cell_index (ci); + + std::map > connections; + + while (br) { + + if (test (skeys::net_key) || test (lkeys::net_key)) { + read_net (l2n, circuit, layers); + } else if (test (skeys::pin_key) || test (lkeys::pin_key)) { + read_pin (l2n, circuit); + } else if (test (skeys::device_key) || test (lkeys::device_key)) { + std::list conn; + db::CellInstArray ia = read_device (l2n, circuit, conn); + connections[ia] = conn; + } else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) { + std::list conn; + db::CellInstArray ia = read_subcircuit (l2n, circuit, conn); + connections[ia] = conn; + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (net, pin, device or circuit expected)"))); + } + + } + br.done (); + + db::Cell &ccell = ly->cell (ci); + + // connections needs to be made after the instances (because in a readonly Instances container + // the Instance pointers will invalidate when new instances are added) + for (db::Cell::const_iterator i = ccell.begin (); ! i.at_end (); ++i) { + std::map >::const_iterator c = connections.find (i->cell_inst ()); + if (c != connections.end ()) { + for (std::list::const_iterator j = c->second.begin (); j != c->second.end (); ++j) { + l2n->net_clusters ().clusters_per_cell (ci).add_connection (j->from_cluster, db::ClusterInstance (j->to_cluster, *i)); + } + } + } + + } else if (test (skeys::device_key) || test (lkeys::device_key)) { + + Brace br (this); + std::string name; + read_word_or_quoted (name); + + db::DeviceModel *dm = new db::DeviceModel (); + dm->set_name (name); + l2n->netlist ()->add_device_model (dm); + + db::cell_index_type ci = l2n->internal_layout ()->add_cell (name.c_str ()); + dm->set_cell_index (ci); + + std::string cls; + read_word_or_quoted (cls); + + db::DeviceClass *dc = 0; + for (db::Netlist::device_class_iterator i = l2n->netlist ()->begin_device_classes (); i != l2n->netlist ()->end_device_classes (); ++i) { + if (i->name () == cls) { + dc = i.operator-> (); + } + } + + // use a generic device class unless the right one is registered already. + bool gen_dc = (dc == 0); + if (gen_dc) { + dc = new db::DeviceClass (); + dc->set_name (cls); + l2n->netlist ()->add_device_class (dc); + } + + dm->set_device_class (dc); + + while (br) { + + if (test (skeys::terminal_key) || test (lkeys::terminal_key)) { + read_abstract_terminal (l2n, dm, gen_dc ? dc : 0, layers); + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device abstract definition (terminal expected)"))); + } + + } + + br.done (); + + } + + } +} + +std::pair +LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n, Layers &layers) +{ + std::string lname; + + if (test (skeys::rect_key) || test (lkeys::rect_key)) { + + Brace br (this); + + read_word_or_quoted (lname); + unsigned int lid = l2n->layer_of (layers.layer (lname)); + + db::Coord l = read_coord (); + db::Coord b = read_coord (); + db::Coord r = read_coord (); + db::Coord t = read_coord (); + db::Box box (l, b, r, t); + + br.done (); + + return std::make_pair (lid, db::PolygonRef (db::Polygon (box), l2n->internal_layout ()->shape_repository ())); + + } else if (test (skeys::polygon_key) || test (lkeys::polygon_key)) { + + Brace br (this); + + read_word_or_quoted (lname); + unsigned int lid = l2n->layer_of (layers.layer (lname)); + + std::vector pt; + + while (br) { + db::Coord x = read_coord (); + db::Coord y = read_coord (); + pt.push_back (db::Point (x, y)); + } + + br.done (); + + db::Polygon poly; + poly.assign_hull (pt.begin (), pt.end ()); + return std::make_pair (lid, db::PolygonRef (poly, l2n->internal_layout ()->shape_repository ())); + + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside net or terminal definition (polygon or rect expected)"))); + } +} + +void +LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, Layers &layers) +{ + Brace br (this); + + std::string name; + read_word_or_quoted (name); + + db::Net *net = new db::Net (); + net->set_name (name); + circuit->add_net (net); + + db::connected_clusters &cc = l2n->net_clusters ().clusters_per_cell (circuit->cell_index ()); + db::local_cluster &lc = *cc.insert (); + net->set_cluster_id (lc.id ()); + + db::Cell &cell = l2n->internal_layout ()->cell (circuit->cell_index ()); + + while (br) { + std::pair pr = read_geometry (l2n, layers); + lc.add (pr.second, pr.first); + cell.shapes (pr.first).insert (pr.second); + } + + br.done (); +} + +void +LayoutToNetlistStandardReader::read_pin (db::LayoutToNetlist * /*l2n*/, db::Circuit *circuit) +{ + Brace br (this); + std::string name; + read_word_or_quoted (name); + std::string netname; + read_word_or_quoted (netname); + br.done (); + + const db::Pin &pin = circuit->add_pin (name); + + db::Net *net = circuit->net_by_name (netname); + if (!net) { + throw tl::Exception (tl::to_string (tr ("Not a valid net name: ")) + netname); + } + + circuit->connect_pin (pin.id (), net); +} + +db::CellInstArray +LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs) +{ + Brace br (this); + + std::string name; + read_word_or_quoted (name); + + std::string dmname; + read_word_or_quoted (dmname); + + db::DeviceModel *dm = 0; + for (db::Netlist::device_model_iterator i = l2n->netlist ()->begin_device_models (); i != l2n->netlist ()->end_device_models (); ++i) { + if (i->name () == dmname) { + dm = i.operator-> (); + } + } + + if (! dm) { + throw tl::Exception (tl::to_string (tr ("Not a valid device model name: ")) + dmname); + } + + db::Device *device = new db::Device (); + device->set_device_class (const_cast (dm->device_class ())); + device->set_device_model (dm); + device->set_name (name); + circuit->add_device (device); + + db::Coord x = 0, y = 0; + + while (br) { + + if (test (skeys::location_key) || test (lkeys::location_key)) { + + Brace br2 (this); + x = read_coord (); + y = read_coord (); + br2.done (); + + } else if (test (skeys::terminal_key) || test (lkeys::terminal_key)) { + + Brace br2 (this); + std::string tname; + read_word_or_quoted (tname); + std::string netname; + read_word_or_quoted (netname); + br2.done (); + + size_t tid = std::numeric_limits::max (); + const std::vector &td = dm->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + if (t->name () == tname) { + tid = t->id (); + break; + } + } + + if (tid == std::numeric_limits::max ()) { + throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + tname + tl::to_string (tr (" for device class: ")) + dm->device_class ()->name ()); + } + + db::Net *net = circuit->net_by_name (netname); + if (!net) { + throw tl::Exception (tl::to_string (tr ("Not a valid net name: ")) + netname); + } + + device->connect_terminal (tid, net); + refs.push_back (Connections (net->cluster_id (), dm->cluster_id_for_terminal (tid))); + + } else if (test (skeys::param_key) || test (lkeys::param_key)) { + + Brace br2 (this); + std::string pname; + read_word_or_quoted (pname); + double value = read_double (); + br2.done (); + + size_t pid = std::numeric_limits::max (); + const std::vector &pd = dm->device_class ()->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (p->name () == pname) { + pid = p->id (); + break; + } + } + + // if no parameter with this name exists, create one + if (pid == std::numeric_limits::max ()) { + // TODO: this should only happen for generic devices + db::DeviceClass *dc = const_cast (dm->device_class ()); + pid = dc->add_parameter_definition (db::DeviceParameterDefinition (pname, std::string ())).id (); + } + + device->set_parameter_value (pid, value); + + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device definition (location, param or terminal expected)"))); + } + + } + + double dbu = l2n->internal_layout ()->dbu (); + device->set_position (db::DPoint (dbu * x, dbu * y)); + + br.done (); + + // make device cell instance + db::CellInstArray inst (db::CellInst (dm->cell_index ()), db::Trans (db::Vector (x, y))); + db::Cell &ccell = l2n->internal_layout ()->cell (circuit->cell_index ()); + ccell.insert (inst); + + return inst; +} + +db::CellInstArray +LayoutToNetlistStandardReader::read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs) +{ + Brace br (this); + + std::string name; + read_word_or_quoted (name); + + std::string xname; + read_word_or_quoted (xname); + + db::Circuit *circuit_ref = l2n->netlist ()->circuit_by_name (xname); + if (! circuit_ref) { + throw tl::Exception (tl::to_string (tr ("Not a valid device circuit name: ")) + xname); + } + + db::SubCircuit *subcircuit = new db::SubCircuit (circuit_ref); + subcircuit->set_name (name); + circuit->add_subcircuit (subcircuit); + + db::Coord x = 0, y = 0; + bool mirror = false; + double angle = 0; + double mag = 1.0; + + db::InstElement ie; + bool inst_made = false; + + while (br) { + + if (test (skeys::location_key) || test (lkeys::location_key)) { + + Brace br2 (this); + x = read_coord (); + y = read_coord (); + br2.done (); + + if (inst_made) { + throw tl::Exception (tl::to_string (tr ("location key must come before pin key in subcircuit definition"))); + } + + } else if (test (skeys::rotation_key) || test (lkeys::rotation_key)) { + + Brace br2 (this); + angle = read_double (); + br2.done (); + + if (inst_made) { + throw tl::Exception (tl::to_string (tr ("rotation key must come before pin key in subcircuit definition"))); + } + + } else if (test (skeys::mirror_key) || test (lkeys::mirror_key)) { + + mirror = true; + if (inst_made) { + throw tl::Exception (tl::to_string (tr ("mirror key must come before pin key in subcircuit definition"))); + } + + } else if (test (skeys::scale_key) || test (lkeys::scale_key)) { + + Brace br2 (this); + mag = read_double (); + br2.done (); + + if (inst_made) { + throw tl::Exception (tl::to_string (tr ("scale key must come before pin key in subcircuit definition"))); + } + + } else if (test (skeys::pin_key) || test (lkeys::pin_key)) { + + Brace br2 (this); + std::string pname; + read_word_or_quoted (pname); + std::string netname; + read_word_or_quoted (netname); + br2.done (); + + const db::Pin *sc_pin = circuit_ref->pin_by_name (pname); + if (! sc_pin) { + throw tl::Exception (tl::to_string (tr ("Not a valid pin name: ")) + pname + tl::to_string (tr (" for circuit: ")) + circuit_ref->name ()); + } + + db::Net *net = circuit->net_by_name (netname); + if (!net) { + throw tl::Exception (tl::to_string (tr ("Not a valid net name: ")) + netname); + } + + subcircuit->connect_pin (sc_pin->id (), net); + db::Net *sc_net = circuit_ref->net_for_pin (sc_pin->id ()); + if (sc_net) { + refs.push_back (Connections (net->cluster_id (), sc_net->cluster_id ())); + } + + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside subcircuit definition (location, rotation, mirror, scale or pin expected)"))); + } + + } + + br.done (); + + double dbu = l2n->internal_layout ()->dbu (); + subcircuit->set_trans (db::DCplxTrans (mag, angle, mirror, db::DVector (dbu * x, dbu * y))); + + db::CellInstArray inst (db::CellInst (circuit_ref->cell_index ()), db::ICplxTrans (mag, angle, mirror, db::Vector (x, y))); + db::Cell &ccell = l2n->internal_layout ()->cell (circuit->cell_index ()); + ccell.insert (inst); + + return inst; +} + +void +LayoutToNetlistStandardReader::read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceModel *dm, db::DeviceClass *dc, Layers &layers) +{ + Brace br (this); + + std::string name; + read_word_or_quoted (name); + + size_t tid = std::numeric_limits::max (); + const std::vector &td = dm->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + if (t->name () == name) { + tid = t->id (); + break; + } + } + + // create a terminal unless one with this name already exists + if (tid == std::numeric_limits::max ()) { + if (! dc) { + throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + name + tl::to_string (tr (" for device class: ")) + dm->device_class ()->name ()); + } + db::DeviceTerminalDefinition new_td (name, std::string ()); + tid = dc->add_terminal_definition (new_td).id (); + } + + db::connected_clusters &cc = l2n->net_clusters ().clusters_per_cell (dm->cell_index ()); + db::local_cluster &lc = *cc.insert (); + dm->set_cluster_id_for_terminal (tid, lc.id ()); + + db::Cell &cell = l2n->internal_layout ()->cell (dm->cell_index ()); + + while (br) { + std::pair pr = read_geometry (l2n, layers); + lc.add (pr.second, pr.first); + cell.shapes (pr.first).insert (pr.second); + } + + br.done (); +} } diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h index 038820ac2..6fe9a566a 100644 --- a/src/db/db/dbLayoutToNetlistReader.h +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -24,10 +24,83 @@ #define HDR_dbLayoutToNetlistReader #include "dbCommon.h" +#include "dbPolygon.h" +#include "dbCell.h" +#include "tlStream.h" namespace db { -// ... +namespace l2n_std_reader { + class Layers; + class Brace; +} + +class LayoutToNetlist; +class Circuit; +class Cell; +class DeviceModel; +class DeviceClass; + +/** + * @brief The base class for a LayoutToNetlist writer + */ +class DB_PUBLIC LayoutToNetlistReaderBase +{ +public: + LayoutToNetlistReaderBase () { } + virtual ~LayoutToNetlistReaderBase () { } + + virtual void read (db::LayoutToNetlist *l2n) = 0; +}; + +/** + * @brief The standard writer + */ +class DB_PUBLIC LayoutToNetlistStandardReader + : public LayoutToNetlistReaderBase +{ +public: + LayoutToNetlistStandardReader (tl::InputStream &stream); + + void read (db::LayoutToNetlist *l2n); + +private: + friend class l2n_std_reader::Brace; + typedef l2n_std_reader::Brace Brace; + typedef l2n_std_reader::Layers Layers; + + struct Connections + { + Connections (size_t _from_cluster, size_t _to_cluster) + : from_cluster (_from_cluster), to_cluster (_to_cluster) + { } + + size_t from_cluster, to_cluster; + }; + + tl::TextInputStream m_stream; + std::string m_path; + std::string m_line; + tl::Extractor m_ex; + + void do_read (db::LayoutToNetlist *l2n); + + bool test (const std::string &token); + void expect (const std::string &token); + void read_word_or_quoted(std::string &s); + int read_int (); + db::Coord read_coord (); + double read_double (); + bool at_end (); + void skip (); + + void read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, Layers &layers); + void read_pin (db::LayoutToNetlist *l2n, db::Circuit *circuit); + db::CellInstArray read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs); + db::CellInstArray read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs); + void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceModel *dm, db::DeviceClass *dc, Layers &layers); + std::pair read_geometry (db::LayoutToNetlist *l2n, Layers &layers); +}; } diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index b77bd8f1c..a84c9289c 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -22,107 +22,42 @@ #include "dbLayoutToNetlistWriter.h" #include "dbLayoutToNetlist.h" +#include "dbLayoutToNetlistFormatDefs.h" namespace db { +namespace l2n_std_format +{ // ------------------------------------------------------------------------------------------- -// LayoutToNetlistStandardWriter implementation +// std_writer_impl implementation -/** - * Comments are introduced by hash: # ... - * Names are words (alphanumerical plus "$", "_", ".") or enclosed in single or double quotes. - * Escape character is backslash. - * Separator is either , or whitespace. Keywords and names are case sensitive. - * Short keys are provided for compacter representation. Short keys can be - * non-alpha (e.g. "*") or empty. - * Single-valued attributes can be given without brackets. - * All dimensions are in units of database unit. - * The file follows the declaration-before-use principle - * (circuits before subcircuits, nets before use ...) - * - * Global statements: - * - * version() - file format version - * description() - an arbitrary description text - * unit() - specifies the database unit [short key: U] - * top() - specifies the name of the top circuit [short key: T] - * layer() - define a layer [short key: L] - * connect( ...) - connects layer1 with the following layers [short key: C] - * global( ...) - connects a layer with the given global nets [short key: G] - * circuit( [circuit-def]) - circuit (cell) [short key: X] - * device( [device-abstract-def]) - * - device abstract [short key: D] - * - * [circuit-def]: - * - * net( [geometry-def]) - net geometry [short key: N] - * A net declaration shall be there also if no geometry - * is present - * pin( ) - outgoing pin connection [short key: P] - * device( [device-def]) - * - device with connections [short key: D] - * subcircuit( [subcircuit-def]) - * - subcircuit with connections [short key: X] - * - * [geometry-def]: - * - * polygon( ...) - defines a polygon [short key: P] - * rect( ) - * - defines a rectangle [short key: R] - * - * [device-abstract-def]: - * - * terminal( [geometry-def]) - * - specifies the terminal geometry [short key: empty] - * - * [device-def]: - * - * param( ) - defines a parameter [short key P] - * abstract() - links to a geometrical device abstract on top level [short key A] - * terminal( ) - * - specifies connection of the terminal with - * a net (short key: empty) - * location( ) - location of the device [short key L] - * - * [subcircuit-def]: - * - * location( ) - location of the subcircuit [short key L] - * rotation() - rotation angle [short key O] - * mirror - if specified, the instance is mirrored before rotation [short key M] - * scale() - magnification [short key *] - * pin( ) - specifies connection of the pin with a net [short key: P] - */ +template +class std_writer_impl +{ +public: + std_writer_impl (tl::OutputStream &stream); -static std::string version_key ("version"); -static std::string description_key ("description"); -static std::string top_key ("top"); -static std::string unit_key ("unit"); -static std::string layer_key ("layer"); -static std::string text_key ("text"); -static std::string connect_key ("connect"); -static std::string global_key ("global"); -static std::string circuit_key ("circuit"); -static std::string net_key ("net"); -static std::string device_key ("device"); -static std::string subcircuit_key ("subcircuit"); -static std::string polygon_key ("polygon"); -static std::string rect_key ("rect"); -static std::string terminal_key ("terminal"); -static std::string abstract_key ("abstract"); -static std::string label_key ("label"); -static std::string param_key ("param"); -static std::string location_key ("location"); -static std::string rotation_key ("rotation"); -static std::string mirror_key ("mirror"); -static std::string scale_key ("scale"); -static std::string pin_key ("pin"); -static std::string indent1 (" "); -static std::string indent2 (" "); -static std::string endl ("\n"); + void write (const db::LayoutToNetlist *l2n); -LayoutToNetlistStandardWriter::LayoutToNetlistStandardWriter (tl::OutputStream &stream) +private: + tl::OutputStream *mp_stream; + + void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit); + void write (const db::LayoutToNetlist *l2n, const db::Net &net); + void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit); + void write (const db::LayoutToNetlist *l2n, const db::Device &device); + void write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model); + void write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname); +}; + +static const std::string endl ("\n"); +static const std::string indent1 (" "); +static const std::string indent2 (" "); + +template +std_writer_impl::std_writer_impl (tl::OutputStream &stream) : mp_stream (&stream) { // .. nothing yet .. @@ -138,7 +73,8 @@ static std::string name_for_layer (const db::Layout *layout, unsigned int l) } } -void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n) { bool any = false; @@ -150,17 +86,17 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) *mp_stream << "# General section" << endl; *mp_stream << "# Lists general definitions." << endl << endl; if (version > 0) { - *mp_stream << version_key << "(" << version << ")" << endl; + *mp_stream << Keys::version_key << "(" << version << ")" << endl; } - *mp_stream << top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; - *mp_stream << unit_key << "(" << ly->dbu () << ")" << endl; + *mp_stream << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; + *mp_stream << Keys::unit_key << "(" << ly->dbu () << ")" << endl; *mp_stream << endl << "# Layer section" << endl; *mp_stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl; *mp_stream << endl << "# Mask layers" << endl; for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { - *mp_stream << layer_key << "(" << name_for_layer (ly, *l) << ")" << endl; + *mp_stream << Keys::layer_key << "(" << name_for_layer (ly, *l) << ")" << endl; } *mp_stream << endl << "# Mask layer connectivity" << endl; @@ -169,7 +105,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l); db::Connectivity::layer_iterator cb = l2n->connectivity ().begin_connected (*l); if (cb != ce) { - *mp_stream << connect_key << "(" << name_for_layer (ly, *l); + *mp_stream << Keys::connect_key << "(" << name_for_layer (ly, *l); for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) { *mp_stream << " " << name_for_layer (ly, *c); } @@ -188,7 +124,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) *mp_stream << endl << "# Global nets and connectivity" << endl; any = true; } - *mp_stream << global_key << "(" << name_for_layer (ly, *l); + *mp_stream << Keys::global_key << "(" << name_for_layer (ly, *l); for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) { *mp_stream << " " << tl::to_word_or_quoted_string (l2n->connectivity ().global_net_name (*g)); } @@ -203,7 +139,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) } for (db::Netlist::const_device_model_iterator m = nl->begin_device_models (); m != nl->end_device_models (); ++m) { if (m->device_class ()) { - *mp_stream << device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl; + *mp_stream << Keys::device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl; write (l2n, *m); *mp_stream << ")" << endl; } @@ -211,16 +147,17 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) *mp_stream << endl << "# Circuit section" << endl; *mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl; - for (db::Netlist::const_top_down_circuit_iterator i = nl->begin_top_down (); i != nl->end_top_down (); ++i) { + for (db::Netlist::const_bottom_up_circuit_iterator i = nl->begin_bottom_up (); i != nl->end_bottom_up (); ++i) { const db::Circuit *x = *i; *mp_stream << endl << "# Circuit " << x->name () << endl; - *mp_stream << circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; + *mp_stream << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; write (l2n, *x); *mp_stream << ")" << endl; } } -void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit) +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit) { if (circuit.begin_nets () != circuit.end_nets ()) { *mp_stream << endl << indent1 << "# Nets with their geometries" << endl; @@ -234,7 +171,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { const db::Net *net = circuit.net_for_pin (p->id ()); if (net) { - *mp_stream << indent1 << pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl; + *mp_stream << indent1 << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl; } } } @@ -265,7 +202,8 @@ void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr) } } -void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname) +template +void std_writer_impl::write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname) { db::ICplxTrans t = tr * db::ICplxTrans (s->trans ()); @@ -273,14 +211,14 @@ void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const db::IC if (poly.is_box ()) { db::Box box = t * poly.box (); - *mp_stream << rect_key << "(" << lname; + *mp_stream << Keys::rect_key << "(" << lname; *mp_stream << " " << box.left () << " " << box.bottom (); *mp_stream << " " << box.right () << " " << box.top (); *mp_stream << ")"; } else { - *mp_stream << polygon_key << "(" << lname; + *mp_stream << Keys::polygon_key << "(" << lname; if (poly.holes () > 0) { db::SimplePolygon sp (poly); write_points (*mp_stream, sp, t); @@ -292,7 +230,8 @@ void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const db::IC } } -void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Net &net) +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net &net) { const db::Layout *ly = l2n->internal_layout (); const db::hier_clusters &clusters = l2n->net_clusters (); @@ -319,7 +258,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const } else { if (! any) { - *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; + *mp_stream << indent1 << Keys::net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; any = true; } @@ -340,28 +279,30 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const if (any) { *mp_stream << indent1 << ")" << endl; } else { - *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << ")" << endl; + *mp_stream << indent1 << Keys::net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << ")" << endl; } } -void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit) +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit) { const db::Layout *ly = l2n->internal_layout (); double dbu = ly->dbu (); - *mp_stream << indent1 << subcircuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.expanded_name ()); + *mp_stream << indent1 << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.expanded_name ()); + *mp_stream << " " << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ()); const db::DCplxTrans &tr = subcircuit.trans (); if (tr.is_mag ()) { - *mp_stream << " " << scale_key << "(" << tr.mag () << ")"; + *mp_stream << " " << Keys::scale_key << "(" << tr.mag () << ")"; } if (tr.is_mirror ()) { - *mp_stream << " " << mirror_key; + *mp_stream << " " << Keys::mirror_key; } if (fabs (tr.angle ()) > 1e-6) { - *mp_stream << " " << rotation_key << "(" << tr.angle () << ")"; + *mp_stream << " " << Keys::rotation_key << "(" << tr.angle () << ")"; } - *mp_stream << " " << location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")"; + *mp_stream << " " << Keys::location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")"; // each pin in one line for more than a few pins bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1); @@ -378,7 +319,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const } else { *mp_stream << " "; } - *mp_stream << pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")"; + *mp_stream << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")"; if (separate_lines) { *mp_stream << endl; } @@ -392,7 +333,8 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const *mp_stream << ")" << endl; } -void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model) +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model) { const std::vector &td = device_model.device_class ()->terminal_definitions (); @@ -402,7 +344,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { - *mp_stream << indent1 << terminal_key << "(" << t->name () << endl; + *mp_stream << indent1 << Keys::terminal_key << "(" << t->name () << endl; for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { @@ -422,31 +364,55 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const } } -void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Device &device) +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Device &device) { const db::Layout *ly = l2n->internal_layout (); double dbu = ly->dbu (); - *mp_stream << indent1 << device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ()); - *mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl; + *mp_stream << indent1 << Keys::device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ()); - *mp_stream << indent2 << location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl; + tl_assert (device.device_model () != 0); + *mp_stream << " " << tl::to_word_or_quoted_string (device.device_model ()->name ()) << endl; - if (device.device_model ()) { - *mp_stream << indent2 << abstract_key << "(" << tl::to_word_or_quoted_string (device.device_model ()->name ()) << ")" << endl; - } + *mp_stream << indent2 << Keys::location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl; const std::vector &pd = device.device_class ()->parameter_definitions (); for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - *mp_stream << indent2 << param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl; + *mp_stream << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl; } const std::vector &td = device.device_class ()->terminal_definitions (); for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { - *mp_stream << indent2 << terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (device.net_for_terminal (i->id ())->expanded_name ()) << ")" << endl; + const db::Net *net = device.net_for_terminal (i->id ()); + if (net) { + *mp_stream << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl; + } } *mp_stream << indent1 << ")" << endl; } } + +// ------------------------------------------------------------------------------------------- +// LayoutToNetlistStandardWriter implementation + +LayoutToNetlistStandardWriter::LayoutToNetlistStandardWriter (tl::OutputStream &stream, bool short_version) + : mp_stream (&stream), m_short_version (short_version) +{ + // .. nothing yet .. +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) +{ + if (m_short_version) { + l2n_std_format::std_writer_impl > writer (*mp_stream); + writer.write (l2n); + } else { + l2n_std_format::std_writer_impl > writer (*mp_stream); + writer.write (l2n); + } +} + +} diff --git a/src/db/db/dbLayoutToNetlistWriter.h b/src/db/db/dbLayoutToNetlistWriter.h index 47ad24102..117cf62a3 100644 --- a/src/db/db/dbLayoutToNetlistWriter.h +++ b/src/db/db/dbLayoutToNetlistWriter.h @@ -24,18 +24,12 @@ #define HDR_dbLayoutToNetlistWriter #include "dbCommon.h" -#include "dbPolygon.h" #include "tlStream.h" namespace db { class LayoutToNetlist; -class Net; -class Circuit; -class SubCircuit; -class Device; -class DeviceModel; /** * @brief The base class for a LayoutToNetlist writer @@ -56,19 +50,13 @@ class DB_PUBLIC LayoutToNetlistStandardWriter : public LayoutToNetlistWriterBase { public: - LayoutToNetlistStandardWriter (tl::OutputStream &stream); + LayoutToNetlistStandardWriter (tl::OutputStream &stream, bool short_version); void write (const db::LayoutToNetlist *l2n); private: tl::OutputStream *mp_stream; - - void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit); - void write (const db::LayoutToNetlist *l2n, const db::Net &net); - void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit); - void write (const db::LayoutToNetlist *l2n, const db::Device &device); - void write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model); - void write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname); + bool m_short_version; }; } diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 87db486d6..19b1a4803 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -307,20 +307,16 @@ void NetlistDeviceExtractor::push_new_devices () ps.insert (std::make_pair (m_terminal_id_propname_id, tl::Variant (t->first))); db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); - // initialize the local cluster (will not be extracted) - db::local_cluster *lc = cc.insert (); - lc->add_attr (pi); - dm->set_cluster_id_for_terminal (t->first, lc->id ()); - - // build the cell shapes and local cluster + // build the cell shapes for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) { + db::Shapes &shapes = device_cell.shapes (l->first); for (std::vector::const_iterator s = l->second.begin (); s != l->second.end (); ++s) { db::PolygonRef pr = *s; pr.transform (db::PolygonRef::trans_type (-disp)); shapes.insert (db::PolygonRefWithProperties (pr, pi)); - lc->add (*s, l->first); } + } } diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 8f88932e2..e4881f0db 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -63,18 +63,44 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); } + // reverse lookup for DeviceModel vs. cell index + std::map device_models; + for (db::Netlist::device_model_iterator dm = nl.begin_device_models (); dm != nl.end_device_models (); ++dm) { + device_models.insert (std::make_pair (dm->cell_index (), dm.operator-> ())); + } + std::map > pins_per_cluster_per_cell; for (db::Layout::bottom_up_const_iterator cid = mp_layout->begin_bottom_up (); cid != mp_layout->end_bottom_up (); ++cid) { - if (db::NetlistDeviceExtractor::is_device_cell (*mp_layout, *cid)) { - continue; - } - const connected_clusters_type &clusters = mp_clusters->clusters_per_cell (*cid); if (clusters.empty ()) { continue; } + std::map::const_iterator dmc = device_models.find (*cid); + if (dmc != device_models.end ()) { + + // make the terminal to cluster ID connections for the device model from the device cells + + if (m_terminal_annot_name_id.first) { + + for (connected_clusters_type::const_iterator dc = clusters.begin (); dc != clusters.end (); ++dc) { + for (local_cluster_type::attr_iterator a = dc->begin_attr (); a != dc->end_attr (); ++a) { + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_terminal_annot_name_id.second) { + dmc->second->set_cluster_id_for_terminal (j->second.to (), dc->id ()); + } + } + } + } + + } + + continue; + + } + // a cell makes a new circuit (or uses an existing one) db::Circuit *circuit = 0; diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 3ba88b8c7..c8c0eb80d 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -60,10 +60,10 @@ static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMappin l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } -static void write_l2n (const db::LayoutToNetlist *l2n, const std::string &path) +static void write_l2n (const db::LayoutToNetlist *l2n, const std::string &path, bool short_format) { tl::OutputStream stream (path); - db::LayoutToNetlistStandardWriter writer (stream); + db::LayoutToNetlistStandardWriter writer (stream, short_format); writer.write (l2n); } @@ -277,7 +277,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "This variant accepts a database-unit location. The location is given in the\n" "coordinate space of the initial cell.\n" ) + - gsi::method_ext ("write", &write_l2n, gsi::arg ("path"), + gsi::method_ext ("write", &write_l2n, gsi::arg ("path"), gsi::arg ("short_format", false), "@brief Writes the extracted netlist to a file.\n" "This method employs the native format of KLayout.\n" ), diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc new file mode 100644 index 000000000..9e37f997a --- /dev/null +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -0,0 +1,66 @@ + +/* + + 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 "dbLayoutToNetlist.h" +#include "dbLayoutToNetlistReader.h" +#include "dbLayoutToNetlistWriter.h" +#include "dbStream.h" +#include "dbCommonReader.h" +#include "dbNetlistDeviceExtractorClasses.h" + +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" + +TEST(1_ReaderBasic) +{ + db::Layout ly; + + db::Cell &tc = ly.cell (ly.add_cell ("TOP")); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt"); + tl::InputStream is_in (in_path); + + db::LayoutToNetlistStandardReader reader (is_in); + reader.read (&l2n); + + // verify against the input + + std::string path = tmp_file ("tmp_l2nreader_1.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 4484d10a6..195f9b2c2 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -168,7 +168,7 @@ TEST(1_WriterBasic) std::string path = tmp_file ("tmp_l2nwriter_1.txt"); { tl::OutputStream stream (path); - db::LayoutToNetlistStandardWriter writer (stream); + db::LayoutToNetlistStandardWriter writer (stream, false); writer.write (&l2n); } diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 5bed164ca..460b684a1 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -64,7 +64,8 @@ SOURCES = \ dbNetlistDeviceExtractorTests.cc \ dbNetlistDeviceClassesTests.cc \ dbLayoutToNetlistTests.cc \ - dbLayoutToNetlistWriterTests.cc + dbLayoutToNetlistWriterTests.cc \ + dbLayoutToNetlistReaderTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/testdata/algo/l2n_writer_au.txt b/testdata/algo/l2n_writer_au.txt index c8f789c19..7329fa2ce 100644 --- a/testdata/algo/l2n_writer_au.txt +++ b/testdata/algo/l2n_writer_au.txt @@ -37,10 +37,10 @@ connect(nsd diff_cont nsd) # Device abstracts list the pin shapes of the devices. device(D$PMOS PMOS terminal(S - rect(poly -125 -475 125 475) + rect(psd -650 -475 -125 475) ) terminal(G - rect(psd -650 -475 -125 475) + rect(poly -125 -475 125 475) ) terminal(D rect(psd 125 -475 675 475) @@ -48,10 +48,10 @@ device(D$PMOS PMOS ) device(D$PMOS$1 PMOS terminal(S - rect(poly -125 -475 125 475) + rect(psd -675 -475 -125 475) ) terminal(G - rect(psd -675 -475 -125 475) + rect(poly -125 -475 125 475) ) terminal(D rect(psd 125 -475 650 475) @@ -59,10 +59,10 @@ device(D$PMOS$1 PMOS ) device(D$NMOS NMOS terminal(S - rect(poly -125 -475 125 475) + rect(nsd -650 -475 -125 475) ) terminal(G - rect(nsd -650 -475 -125 475) + rect(poly -125 -475 125 475) ) terminal(D rect(nsd 125 -475 675 475) @@ -70,10 +70,10 @@ device(D$NMOS NMOS ) device(D$NMOS$1 NMOS terminal(S - rect(poly -125 -475 125 475) + rect(nsd -675 -475 -125 475) ) terminal(G - rect(nsd -675 -475 -125 475) + rect(poly -125 -475 125 475) ) terminal(D rect(nsd 125 -475 650 475) @@ -83,6 +83,135 @@ device(D$NMOS$1 NMOS # Circuit section # Circuits are the hierarchical building blocks of the netlist. +# Circuit INV2 +circuit(INV2 + + # Nets with their geometries + net(IN + rect(poly -525 -250 -275 2250) + rect(poly -1700 1620 -400 1980) + rect(poly -525 -800 -275 800) + rect(poly -525 -475 -275 475) + rect(poly -525 2000 -275 3600) + rect(poly -525 2325 -275 3275) + rect(poly_lbl -801 1799 -799 1801) + rect(poly_cont -1630 1690 -1410 1910) + ) + net($2 + rect(poly 275 -250 525 2250) + rect(poly 220 820 580 1180) + rect(poly 275 2000 525 3600) + rect(poly 275 2325 525 3275) + rect(poly 275 -800 525 800) + rect(poly 275 -475 525 475) + rect(diff_cont -910 2490 -690 2710) + rect(diff_cont -910 2890 -690 3110) + rect(diff_cont -910 -310 -690 -90) + rect(diff_cont -910 90 -690 310) + rect(poly_cont 290 890 510 1110) + rect(metal1 -800 820 580 1180) + rect(metal1 -980 -420 -620 2420) + rect(metal1 -980 2420 -620 3180) + rect(metal1 -980 -380 -620 380) + rect(psd -1050 2325 -525 3275) + rect(psd -1050 2325 -525 3275) + rect(nsd -1050 -475 -525 475) + rect(nsd -1050 -475 -525 475) + ) + net(OUT + rect(diff_cont 690 2890 910 3110) + rect(diff_cont 690 2490 910 2710) + rect(diff_cont 690 90 910 310) + rect(diff_cont 690 -310 910 -90) + polygon(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20) + rect(metal1 620 2420 980 3180) + rect(metal1 620 -380 980 380) + rect(metal1_lbl 799 1799 801 1801) + rect(psd 525 2325 1050 3275) + rect(psd 525 2325 1050 3275) + rect(nsd 525 -475 1050 475) + rect(nsd 525 -475 1050 475) + ) + net($4 + rect(diff_cont -110 -310 110 -90) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 -310 110 -90) + rect(metal1 -180 -380 180 380) + rect(metal1 -180 -380 180 380) + rect(via1 -125 -325 125 -75) + rect(via1 -125 75 125 325) + rect(metal2 -1400 -450 1400 450) + rect(nsd -275 -475 275 475) + rect(nsd -275 -475 275 475) + rect(nsd -275 -475 275 475) + ) + net($5 + rect(diff_cont -110 2490 110 2710) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2490 110 2710) + rect(metal1 -180 2420 180 3180) + rect(metal1 -180 2420 180 3180) + rect(via1 -125 2475 125 2725) + rect(via1 -125 2875 125 3125) + rect(metal2 -1400 2350 1400 3250) + rect(psd -275 2325 275 3275) + rect(psd -275 2325 275 3275) + rect(psd -275 2325 275 3275) + ) + + # Outgoing pins and their connections to nets + pin(IN IN) + pin($1 $2) + pin(OUT OUT) + pin($3 $4) + pin($4 $5) + + # Devices and their connections + device($1 D$PMOS + location(-400 2800) + param(L 0.25) + param(W 0.95) + param(AS 0.49875) + param(AD 0.26125) + terminal(S $2) + terminal(G IN) + terminal(D $5) + ) + device($2 D$PMOS$1 + location(400 2800) + param(L 0.25) + param(W 0.95) + param(AS 0.26125) + param(AD 0.49875) + terminal(S $5) + terminal(G $2) + terminal(D OUT) + ) + device($3 D$NMOS + location(-400 0) + param(L 0.25) + param(W 0.95) + param(AS 0.49875) + param(AD 0.26125) + terminal(S $2) + terminal(G IN) + terminal(D $4) + ) + device($4 D$NMOS$1 + location(400 0) + param(L 0.25) + param(W 0.95) + param(AS 0.26125) + param(AD 0.49875) + terminal(S $4) + terminal(G $2) + terminal(D OUT) + ) + +) + # Circuit RINGO circuit(RINGO @@ -280,61 +409,61 @@ circuit(RINGO ) # Subcircuits and their connections - subcircuit($1 location(23760 0) + circuit($1 INV2 location(23760 0) pin(IN $I8) pin($1 FB) pin($3 VSS) pin($4 VDD) ) - subcircuit($2 location(0 0) + circuit($2 INV2 location(0 0) pin(IN FB) pin(OUT $I19) pin($3 VSS) pin($4 VDD) ) - subcircuit($3 location(2640 0) + circuit($3 INV2 location(2640 0) pin(IN $I19) pin(OUT $I1) pin($3 VSS) pin($4 VDD) ) - subcircuit($4 location(5280 0) + circuit($4 INV2 location(5280 0) pin(IN $I1) pin(OUT $I2) pin($3 VSS) pin($4 VDD) ) - subcircuit($5 location(7920 0) + circuit($5 INV2 location(7920 0) pin(IN $I2) pin(OUT $I3) pin($3 VSS) pin($4 VDD) ) - subcircuit($6 location(10560 0) + circuit($6 INV2 location(10560 0) pin(IN $I3) pin(OUT $I4) pin($3 VSS) pin($4 VDD) ) - subcircuit($7 location(13200 0) + circuit($7 INV2 location(13200 0) pin(IN $I4) pin(OUT $I5) pin($3 VSS) pin($4 VDD) ) - subcircuit($8 location(15840 0) + circuit($8 INV2 location(15840 0) pin(IN $I5) pin(OUT $I6) pin($3 VSS) pin($4 VDD) ) - subcircuit($9 location(18480 0) + circuit($9 INV2 location(18480 0) pin(IN $I6) pin(OUT $I7) pin($3 VSS) pin($4 VDD) ) - subcircuit($10 location(21120 0) + circuit($10 INV2 location(21120 0) pin(IN $I7) pin(OUT $I8) pin($3 VSS) @@ -342,136 +471,3 @@ circuit(RINGO ) ) - -# Circuit INV2 -circuit(INV2 - - # Nets with their geometries - net(IN - rect(poly -525 -250 -275 2250) - rect(poly -1700 1620 -400 1980) - rect(poly -525 -800 -275 800) - rect(poly -525 -475 -275 475) - rect(poly -525 2000 -275 3600) - rect(poly -525 2325 -275 3275) - rect(poly_lbl -801 1799 -799 1801) - rect(poly_cont -1630 1690 -1410 1910) - ) - net($2 - rect(poly 275 -250 525 2250) - rect(poly 220 820 580 1180) - rect(poly 275 2000 525 3600) - rect(poly 275 2325 525 3275) - rect(poly 275 -800 525 800) - rect(poly 275 -475 525 475) - rect(diff_cont -910 2490 -690 2710) - rect(diff_cont -910 2890 -690 3110) - rect(diff_cont -910 -310 -690 -90) - rect(diff_cont -910 90 -690 310) - rect(poly_cont 290 890 510 1110) - rect(metal1 -800 820 580 1180) - rect(metal1 -980 -420 -620 2420) - rect(metal1 -980 2420 -620 3180) - rect(metal1 -980 -380 -620 380) - rect(psd -1050 2325 -525 3275) - rect(psd -1050 2325 -525 3275) - rect(nsd -1050 -475 -525 475) - rect(nsd -1050 -475 -525 475) - ) - net(OUT - rect(diff_cont 690 2890 910 3110) - rect(diff_cont 690 2490 910 2710) - rect(diff_cont 690 90 910 310) - rect(diff_cont 690 -310 910 -90) - polygon(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20) - rect(metal1 620 2420 980 3180) - rect(metal1 620 -380 980 380) - rect(metal1_lbl 799 1799 801 1801) - rect(psd 525 2325 1050 3275) - rect(psd 525 2325 1050 3275) - rect(nsd 525 -475 1050 475) - rect(nsd 525 -475 1050 475) - ) - net($4 - rect(diff_cont -110 -310 110 -90) - rect(diff_cont -110 90 110 310) - rect(diff_cont -110 90 110 310) - rect(diff_cont -110 -310 110 -90) - rect(metal1 -180 -380 180 380) - rect(metal1 -180 -380 180 380) - rect(via1 -125 -325 125 -75) - rect(via1 -125 75 125 325) - rect(metal2 -1400 -450 1400 450) - rect(nsd -275 -475 275 475) - rect(nsd -275 -475 275 475) - rect(nsd -275 -475 275 475) - ) - net($5 - rect(diff_cont -110 2490 110 2710) - rect(diff_cont -110 2890 110 3110) - rect(diff_cont -110 2890 110 3110) - rect(diff_cont -110 2490 110 2710) - rect(metal1 -180 2420 180 3180) - rect(metal1 -180 2420 180 3180) - rect(via1 -125 2475 125 2725) - rect(via1 -125 2875 125 3125) - rect(metal2 -1400 2350 1400 3250) - rect(psd -275 2325 275 3275) - rect(psd -275 2325 275 3275) - rect(psd -275 2325 275 3275) - ) - - # Outgoing pins and their connections to nets - pin(IN IN) - pin($1 $2) - pin(OUT OUT) - pin($3 $4) - pin($4 $5) - - # Devices and their connections - device($1 PMOS - location(-400 2800) - abstract(D$PMOS) - param(L 0.25) - param(W 0.95) - param(AS 0.49875) - param(AD 0.26125) - terminal(S $2) - terminal(G IN) - terminal(D $5) - ) - device($2 PMOS - location(400 2800) - abstract(D$PMOS$1) - param(L 0.25) - param(W 0.95) - param(AS 0.26125) - param(AD 0.49875) - terminal(S $5) - terminal(G $2) - terminal(D OUT) - ) - device($3 NMOS - location(-400 0) - abstract(D$NMOS) - param(L 0.25) - param(W 0.95) - param(AS 0.49875) - param(AD 0.26125) - terminal(S $2) - terminal(G IN) - terminal(D $4) - ) - device($4 NMOS - location(400 0) - abstract(D$NMOS$1) - param(L 0.25) - param(W 0.95) - param(AS 0.26125) - param(AD 0.49875) - terminal(S $4) - terminal(G $2) - terminal(D OUT) - ) - -) From a5e2cf58c323af138b6b5828c4471d7193aa0717 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Jan 2019 23:00:19 +0100 Subject: [PATCH 201/335] l2n dump format is leaner (device terminal shapes dropped from nets as they are contained in the device abstracts). Some refactoring. --- src/db/db/dbDeviceModel.cc | 7 +++ src/db/db/dbLayoutToNetlist.cc | 12 ++--- src/db/db/dbLayoutToNetlistWriter.cc | 7 ++- src/db/db/dbNetlist.cc | 17 +++++- src/db/db/dbNetlist.h | 44 +++++++++++++++ src/db/db/dbNetlistDeviceExtractor.cc | 31 +---------- src/db/db/dbNetlistDeviceExtractor.h | 6 --- src/db/db/dbNetlistExtractor.cc | 54 ++++++++++--------- src/db/db/dbNetlistExtractor.h | 7 +++ .../dbLayoutToNetlistReaderTests.cc | 23 ++++++++ src/db/unit_tests/dbNetlistTests.cc | 13 ++++- testdata/algo/l2n_writer_au.txt | 16 ------ 12 files changed, 145 insertions(+), 92 deletions(-) diff --git a/src/db/db/dbDeviceModel.cc b/src/db/db/dbDeviceModel.cc index 69245cc8f..a6d3b9d77 100644 --- a/src/db/db/dbDeviceModel.cc +++ b/src/db/db/dbDeviceModel.cc @@ -22,6 +22,7 @@ #include "dbDeviceModel.h" #include "dbCircuit.h" +#include "dbNetlist.h" namespace db { @@ -71,11 +72,17 @@ void DeviceModel::set_netlist (Netlist *netlist) void DeviceModel::set_name (const std::string &n) { m_name = n; + if (mp_netlist) { + mp_netlist->m_device_model_by_name.invalidate (); + } } void DeviceModel::set_cell_index (db::cell_index_type ci) { m_cell_index = ci; + if (mp_netlist) { + mp_netlist->m_device_model_by_cell_index.invalidate (); + } } size_t DeviceModel::cluster_id_for_terminal (size_t terminal_id) const diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 81127318e..daeda3f53 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -260,13 +260,9 @@ db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell unsigned int layout_index = 0; std::set device_cells; - - if (! with_device_cells) { - const db::Layout &src_layout = m_dss.layout (layout_index); - for (db::Layout::const_iterator c = src_layout.begin (); c != src_layout.end (); ++c) { - if (db::NetlistDeviceExtractor::is_device_cell (src_layout, c->cell_index ())) { - device_cells.insert (c->cell_index ()); - } + if (! with_device_cells && mp_netlist.get ()) { + for (db::Netlist::device_model_iterator i = mp_netlist->begin_device_models (); i != mp_netlist->end_device_models (); ++i) { + device_cells.insert (i->cell_index ()); } } @@ -404,7 +400,7 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & if (cm == cmap.end ()) { const char *name_prefix = 0; - if (db::NetlistDeviceExtractor::is_device_cell (*internal_layout (), subci)) { + if (mp_netlist->device_model_by_cell_index (subci)) { name_prefix = device_cell_name_prefix; } else { name_prefix = cell_name_prefix; diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index a84c9289c..2daa621be 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -149,7 +149,6 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) *mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl; for (db::Netlist::const_bottom_up_circuit_iterator i = nl->begin_bottom_up (); i != nl->end_bottom_up (); ++i) { const db::Circuit *x = *i; - *mp_stream << endl << "# Circuit " << x->name () << endl; *mp_stream << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; write (l2n, *x); *mp_stream << ")" << endl; @@ -233,6 +232,10 @@ void std_writer_impl::write (const db::PolygonRef *s, const db::ICplxTrans template void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net &net) { + if (! l2n->netlist ()) { + throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before extraction has been done"))); + } + const db::Layout *ly = l2n->internal_layout (); const db::hier_clusters &clusters = l2n->net_clusters (); const db::Circuit *circuit = net.circuit (); @@ -251,7 +254,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net // vanish in "purge" but the clusters will still be there we need to recursive into clusters from // unknown cells. db::cell_index_type ci = si.cell_index (); - if (ci != prev_ci && ci != cci && l2n->netlist ()->circuit_by_cell_index (ci)) { + if (ci != prev_ci && ci != cci && (l2n->netlist ()->circuit_by_cell_index (ci) || l2n->netlist ()->device_model_by_cell_index (ci))) { si.skip_cell (); diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index d48fb90bf..bbeb53ca0 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -33,26 +33,33 @@ namespace db Netlist::Netlist () : m_valid_topology (false), m_lock_count (0), m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), - m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits) + m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits), + m_device_model_by_name (this, &Netlist::begin_device_models, &Netlist::end_device_models), + m_device_model_by_cell_index (this, &Netlist::begin_device_models, &Netlist::end_device_models) { m_circuits.changed ().add (this, &Netlist::invalidate_topology); m_circuits.changed ().add (this, &Netlist::circuits_changed); + m_device_models.changed ().add (this, &Netlist::device_models_changed); } Netlist::Netlist (const Netlist &other) : gsi::ObjectBase (other), tl::Object (other), m_valid_topology (false), m_lock_count (0), m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), - m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits) + m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits), + m_device_model_by_name (this, &Netlist::begin_device_models, &Netlist::end_device_models), + m_device_model_by_cell_index (this, &Netlist::begin_device_models, &Netlist::end_device_models) { operator= (other); m_circuits.changed ().add (this, &Netlist::invalidate_topology); m_circuits.changed ().add (this, &Netlist::circuits_changed); + m_device_models.changed ().add (this, &Netlist::device_models_changed); } Netlist::~Netlist () { m_circuits.changed ().remove (this, &Netlist::invalidate_topology); m_circuits.changed ().remove (this, &Netlist::circuits_changed); + m_device_models.changed ().remove (this, &Netlist::device_models_changed); } Netlist &Netlist::operator= (const Netlist &other) @@ -98,6 +105,12 @@ void Netlist::circuits_changed () m_circuit_by_name.invalidate (); } +void Netlist::device_models_changed () +{ + m_device_model_by_cell_index.invalidate (); + m_device_model_by_name.invalidate (); +} + void Netlist::invalidate_topology () { if (m_valid_topology) { diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 739c7888a..cbb822a36 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -349,6 +349,46 @@ public: return m_device_models.end (); } + /** + * @brief Gets the device model with the given name + * + * If no device model with that name exists, null is returned. + */ + DeviceModel *device_model_by_name (const std::string &name) + { + return m_device_model_by_name.object_by (name); + } + + /** + * @brief Gets the device model with the given name (const version) + * + * If no device model with that name exists, null is returned. + */ + const DeviceModel *device_model_by_name (const std::string &name) const + { + return m_device_model_by_name.object_by (name); + } + + /** + * @brief Gets the device model with the given cell index + * + * If no device model with that cell index exists, null is returned. + */ + DeviceModel *device_model_by_cell_index (db::cell_index_type cell_index) + { + return m_device_model_by_cell_index.object_by (cell_index); + } + + /** + * @brief Gets the device model with the given cell index (const version) + * + * If no device model with that cell index exists, null is returned. + */ + const DeviceModel *device_model_by_cell_index (db::cell_index_type cell_index) const + { + return m_device_model_by_cell_index.object_by (cell_index); + } + /** * @brief Purge unused nets * @@ -383,6 +423,7 @@ public: private: friend class Circuit; + friend class DeviceModel; circuit_list m_circuits; device_class_list m_device_classes; @@ -395,10 +436,13 @@ private: size_t m_top_circuits; object_by_attr > m_circuit_by_name; object_by_attr > m_circuit_by_cell_index; + object_by_attr > m_device_model_by_name; + object_by_attr > m_device_model_by_cell_index; void invalidate_topology (); void validate_topology (); void circuits_changed (); + void device_models_changed (); const tl::vector &child_circuits (Circuit *circuit); const tl::vector &parent_circuits (Circuit *circuit); diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 19b1a4803..5749bcb86 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -169,7 +169,7 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: for (std::set::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) { // skip device cells from previous extractions - if (is_device_cell (*ci)) { + if (m_netlist->device_model_by_cell_index (*ci)) { continue; } @@ -223,33 +223,6 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: } } -bool NetlistDeviceExtractor::is_device_cell (const db::Layout &layout, db::cell_index_type ci) -{ - db::properties_id_type pi = layout.cell (ci).prop_id (); - if (pi == 0) { - return false; - } - - std::pair pn = layout.properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_class_property_name ()); - if (! pn.first) { - return false; - } - - const db::PropertiesRepository::properties_set &ps = layout.properties_repository ().properties (pi); - for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { - if (j->first == pn.second) { - return true; - } - } - - return false; -} - -bool NetlistDeviceExtractor::is_device_cell (db::cell_index_type ci) const -{ - return is_device_cell (*mp_layout, ci); -} - void NetlistDeviceExtractor::push_new_devices () { db::VCplxTrans dbu_inv = db::CplxTrans (mp_layout->dbu ()).inverted (); @@ -298,8 +271,6 @@ void NetlistDeviceExtractor::push_new_devices () ps.insert (std::make_pair (m_device_class_propname_id, tl::Variant (mp_device_class->name ()))); device_cell.prop_id (mp_layout->properties_repository ().properties_id (ps)); - db::connected_clusters &cc = mp_clusters->clusters_per_cell (device_cell.cell_index ()); - for (geometry_per_terminal_type::const_iterator t = d->second.begin (); t != d->second.end (); ++t) { // Build a property set for the device terminal ID diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index 0cc045ceb..8c6734c63 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -234,11 +234,6 @@ public: */ static const tl::Variant &device_class_property_name (); - /** - * @brief Returns true, if the given cell is a device cell - */ - static bool is_device_cell (const db::Layout &layout, db::cell_index_type ci); - /** * @brief Performs the extraction * @@ -527,7 +522,6 @@ private: void extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector &layers); void push_new_devices (); - bool is_device_cell (db::cell_index_type ci) const; }; } diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index e4881f0db..5bf37763e 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -63,12 +63,6 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); } - // reverse lookup for DeviceModel vs. cell index - std::map device_models; - for (db::Netlist::device_model_iterator dm = nl.begin_device_models (); dm != nl.end_device_models (); ++dm) { - device_models.insert (std::make_pair (dm->cell_index (), dm.operator-> ())); - } - std::map > pins_per_cluster_per_cell; for (db::Layout::bottom_up_const_iterator cid = mp_layout->begin_bottom_up (); cid != mp_layout->end_bottom_up (); ++cid) { @@ -77,28 +71,11 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect continue; } - std::map::const_iterator dmc = device_models.find (*cid); - if (dmc != device_models.end ()) { - + db::DeviceModel *dm = nl.device_model_by_cell_index (*cid); + if (dm) { // make the terminal to cluster ID connections for the device model from the device cells - - if (m_terminal_annot_name_id.first) { - - for (connected_clusters_type::const_iterator dc = clusters.begin (); dc != clusters.end (); ++dc) { - for (local_cluster_type::attr_iterator a = dc->begin_attr (); a != dc->end_attr (); ++a) { - const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a); - for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { - if (j->first == m_terminal_annot_name_id.second) { - dmc->second->set_cluster_id_for_terminal (j->second.to (), dc->id ()); - } - } - } - } - - } - + make_device_model_connections (dm, clusters); continue; - } // a cell makes a new circuit (or uses an existing one) @@ -151,6 +128,31 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } } +void +NetlistExtractor::make_device_model_connections (db::DeviceModel *dm, const connected_clusters_type &clusters) +{ + // make the terminal to cluster ID connections for the device model from the device cells + + if (m_terminal_annot_name_id.first) { + + for (connected_clusters_type::const_iterator dc = clusters.begin (); dc != clusters.end (); ++dc) { + + for (local_cluster_type::attr_iterator a = dc->begin_attr (); a != dc->end_attr (); ++a) { + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_terminal_annot_name_id.second) { + dm->set_cluster_id_for_terminal (j->second.to (), dc->id ()); + } + } + + } + + } + + } +} + void NetlistExtractor::collect_labels (const connected_clusters_type &clusters, size_t cid, db::Net *net) diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index 5447f63b5..c896d349d 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -37,6 +37,7 @@ class Circuit; class SubCircuit; class Net; class Device; +class DeviceModel; /** * @brief The Netlist Extractor @@ -151,6 +152,12 @@ private: void collect_labels (const connected_clusters_type &clusters, size_t cid, db::Net *net); + + /** + * @brief Makes the terminal to cluster ID connections of the device model + */ + void make_device_model_connections (db::DeviceModel *dm, const connected_clusters_type &clusters); + }; } diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc index 9e37f997a..8aa43816a 100644 --- a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -63,4 +63,27 @@ TEST(1_ReaderBasic) tl::absolute_file_path (path), tl::absolute_file_path (au_path))); } + + +#if 0 + + std::string path = tmp_file ("tmp_l2nwriter_1.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + +#endif } diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 799d1c791..4af8413b8 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -1047,10 +1047,19 @@ TEST(13_DeviceModel) db::Netlist nl; db::DeviceModel *dm = new db::DeviceModel (0, "name"); + nl.add_device_model (dm); + EXPECT_EQ (dm->netlist () == &nl, true); + EXPECT_EQ (dm->device_class () == 0, true); EXPECT_EQ (dm->name (), "name"); + EXPECT_EQ (nl.device_model_by_name ("name") == dm, true); + EXPECT_EQ (nl.device_model_by_name ("name2") == 0, true); + EXPECT_EQ (nl.device_model_by_name ("does_not_exist") == 0, true); dm->set_name ("name2"); EXPECT_EQ (dm->name (), "name2"); + EXPECT_EQ (nl.device_model_by_name ("name") == 0, true); + EXPECT_EQ (nl.device_model_by_name ("name2") == dm, true); + EXPECT_EQ (nl.device_model_by_name ("does_not_exist") == 0, true); dm->set_cluster_id_for_terminal (1, 17); dm->set_cluster_id_for_terminal (0, 42); @@ -1058,10 +1067,10 @@ TEST(13_DeviceModel) EXPECT_EQ (dm->cluster_id_for_terminal (1), size_t (17)); dm->set_cell_index (5); + EXPECT_EQ (nl.device_model_by_cell_index (5) == dm, true); + EXPECT_EQ (nl.device_model_by_cell_index (17) == 0, true); EXPECT_EQ (dm->cell_index (), db::cell_index_type (5)); - nl.add_device_model (dm); - EXPECT_EQ (dm->netlist () == &nl, true); EXPECT_EQ (nl.begin_device_models () == nl.end_device_models (), false); EXPECT_EQ (nl.begin_device_models ()->name (), "name2"); diff --git a/testdata/algo/l2n_writer_au.txt b/testdata/algo/l2n_writer_au.txt index 7329fa2ce..901b8799a 100644 --- a/testdata/algo/l2n_writer_au.txt +++ b/testdata/algo/l2n_writer_au.txt @@ -82,8 +82,6 @@ device(D$NMOS$1 NMOS # Circuit section # Circuits are the hierarchical building blocks of the netlist. - -# Circuit INV2 circuit(INV2 # Nets with their geometries @@ -91,9 +89,7 @@ circuit(INV2 rect(poly -525 -250 -275 2250) rect(poly -1700 1620 -400 1980) rect(poly -525 -800 -275 800) - rect(poly -525 -475 -275 475) rect(poly -525 2000 -275 3600) - rect(poly -525 2325 -275 3275) rect(poly_lbl -801 1799 -799 1801) rect(poly_cont -1630 1690 -1410 1910) ) @@ -101,9 +97,7 @@ circuit(INV2 rect(poly 275 -250 525 2250) rect(poly 220 820 580 1180) rect(poly 275 2000 525 3600) - rect(poly 275 2325 525 3275) rect(poly 275 -800 525 800) - rect(poly 275 -475 525 475) rect(diff_cont -910 2490 -690 2710) rect(diff_cont -910 2890 -690 3110) rect(diff_cont -910 -310 -690 -90) @@ -114,8 +108,6 @@ circuit(INV2 rect(metal1 -980 2420 -620 3180) rect(metal1 -980 -380 -620 380) rect(psd -1050 2325 -525 3275) - rect(psd -1050 2325 -525 3275) - rect(nsd -1050 -475 -525 475) rect(nsd -1050 -475 -525 475) ) net(OUT @@ -128,8 +120,6 @@ circuit(INV2 rect(metal1 620 -380 980 380) rect(metal1_lbl 799 1799 801 1801) rect(psd 525 2325 1050 3275) - rect(psd 525 2325 1050 3275) - rect(nsd 525 -475 1050 475) rect(nsd 525 -475 1050 475) ) net($4 @@ -143,8 +133,6 @@ circuit(INV2 rect(via1 -125 75 125 325) rect(metal2 -1400 -450 1400 450) rect(nsd -275 -475 275 475) - rect(nsd -275 -475 275 475) - rect(nsd -275 -475 275 475) ) net($5 rect(diff_cont -110 2490 110 2710) @@ -157,8 +145,6 @@ circuit(INV2 rect(via1 -125 2875 125 3125) rect(metal2 -1400 2350 1400 3250) rect(psd -275 2325 275 3275) - rect(psd -275 2325 275 3275) - rect(psd -275 2325 275 3275) ) # Outgoing pins and their connections to nets @@ -211,8 +197,6 @@ circuit(INV2 ) ) - -# Circuit RINGO circuit(RINGO # Nets with their geometries From dd39168dc89207c59498531760263491f2d131fd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 20 Jan 2019 02:50:23 +0100 Subject: [PATCH 202/335] WIP: Enabled layout generation from read l2n data. --- src/db/db/dbLayoutToNetlist.cc | 163 +++-- src/db/db/dbLayoutToNetlist.h | 14 +- src/db/db/dbLayoutToNetlistFormatDefs.h | 2 + src/db/db/dbLayoutToNetlistReader.cc | 41 +- src/db/db/dbLayoutToNetlistReader.h | 10 +- src/db/db/dbLayoutToNetlistWriter.cc | 54 +- .../dbLayoutToNetlistReaderTests.cc | 52 +- .../dbLayoutToNetlistWriterTests.cc | 291 ++++++++- testdata/algo/l2n_writer_au.gds | Bin 0 -> 18548 bytes testdata/algo/l2n_writer_au.txt | 19 +- testdata/algo/l2n_writer_au_2.gds | Bin 0 -> 29606 bytes testdata/algo/l2n_writer_au_2.txt | 566 ++++++++++++++++++ testdata/algo/l2n_writer_au_2s.txt | 527 ++++++++++++++++ testdata/algo/l2n_writer_au_s.txt | 444 ++++++++++++++ 14 files changed, 2098 insertions(+), 85 deletions(-) create mode 100644 testdata/algo/l2n_writer_au.gds create mode 100644 testdata/algo/l2n_writer_au_2.gds create mode 100644 testdata/algo/l2n_writer_au_2.txt create mode 100644 testdata/algo/l2n_writer_au_2s.txt create mode 100644 testdata/algo/l2n_writer_au_s.txt diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index daeda3f53..73a9608e9 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -217,6 +217,11 @@ void LayoutToNetlist::extract_netlist () m_netlist_extracted = true; } +void LayoutToNetlist::set_netlist_extracted () +{ + m_netlist_extracted = true; +} + const db::Layout *LayoutToNetlist::internal_layout () const { return &m_dss.const_layout (); @@ -293,19 +298,30 @@ db::Netlist *LayoutToNetlist::make_netlist () return mp_netlist.get (); } +namespace +{ + struct StopOnFirst { }; +} template -static void deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr) +static bool deliver_shape (const db::PolygonRef &, StopOnFirst, const Tr &) +{ + return false; +} + +template +static bool deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr) { if (pr.obj ().is_box ()) { region.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); } else { region.insert (pr.obj ().transformed (pr.trans ()).transformed (tr)); } + return true; } template -static void deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const Tr &tr) +static bool deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const Tr &tr) { if (pr.obj ().is_box ()) { shapes.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); @@ -317,25 +333,51 @@ static void deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const T shapes.insert (pr.obj ().transformed (pr.trans ()).transformed (tr)); } } + return true; } template -static void deliver_shapes_of_net_recursive (const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, To &to) +static bool deliver_shapes_of_net_recursive (const db::Netlist * /*nl*/, const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to) { // deliver the net shapes for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) { - deliver_shape (*rci, to, rci.trans ()); + if (! deliver_shape (*rci, to, tr * rci.trans ())) { + return false; + } } + return true; } template -static void deliver_shapes_of_net_nonrecursive (const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, To &to) +static bool deliver_shapes_of_net_nonrecursive (const db::Netlist *nl, const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to) { - const db::local_cluster &lc = clusters.clusters_per_cell (ci).cluster_by_id (cid); + // NOTE: this scheme will deliver the shapes from the cell, including (!) + // subcells that are purged + + db::cell_index_type prev_ci = ci; + + // deliver the net shapes + for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ) { + + db::cell_index_type cci = rci.cell_index (); + if (cci != prev_ci && cci != ci && (! nl || nl->circuit_by_cell_index (cci) || nl->device_model_by_cell_index (cci))) { + + rci.skip_cell (); + + } else { + + if (! deliver_shape (*rci, to, tr * rci.trans ())) { + return false; + } + prev_ci = cci; + + ++rci; + + } - for (db::local_cluster::shape_iterator s = lc.begin (layer_id); !s.at_end (); ++s) { - deliver_shape (*s, to, db::UnitTrans ()); } + + return true; } void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const @@ -345,9 +387,9 @@ void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_la tl_assert (circuit != 0); if (! recursive) { - deliver_shapes_of_net_nonrecursive (m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, to); + deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to); } else { - deliver_shapes_of_net_recursive (m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, to); + deliver_shapes_of_net_recursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to); } } @@ -360,31 +402,60 @@ db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region std::auto_ptr res (new db::Region ()); if (! recursive) { - deliver_shapes_of_net_nonrecursive (m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, *res); + deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res); } else { - deliver_shapes_of_net_recursive (m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, *res); + deliver_shapes_of_net_recursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res); } return res.release (); } void -LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap) const +LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const { const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); - build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, cell_name_prefix, device_cell_name_prefix, cmap); + build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, &net, net_cell_name_prefix, cell_name_prefix, device_cell_name_prefix, cmap, tr); } void -LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap) const +LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &tc, const std::map &lmap, const db::Net *net, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const { - for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { - deliver_shapes_of_net_nonrecursive (m_net_clusters, ci, cid, layer_of (*l->second), target_cell.shapes (l->first)); + db::Cell *target_cell = &tc; + + if (net_cell_name_prefix) { + + const db::connected_clusters &ccl = m_net_clusters.clusters_per_cell (ci); + + bool any_connections = circuit_cell_name_prefix && ! ccl.connections_for_cluster (cid).empty (); + if (! any_connections) { + + bool consider_cell = any_connections; + for (std::map::const_iterator l = lmap.begin (); l != lmap.end () && !consider_cell; ++l) { + StopOnFirst sof; + consider_cell = deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, sof); + } + + if (! consider_cell) { + // shortcut if cell is empty -> no net cell will be produced + return; + } + + } + + // make a specific cell for the net if requested + + target_cell = &target.cell (target.add_cell ((std::string (net_cell_name_prefix) + net->expanded_name ()).c_str ())); + tc.insert (db::CellInstArray (db::CellInst (target_cell->cell_index ()), db::Trans ())); + } - if (! cell_name_prefix && ! device_cell_name_prefix) { + for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { + deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, target_cell->shapes (l->first)); + } + + if (! circuit_cell_name_prefix && ! device_cell_name_prefix) { return; } @@ -403,7 +474,7 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & if (mp_netlist->device_model_by_cell_index (subci)) { name_prefix = device_cell_name_prefix; } else { - name_prefix = cell_name_prefix; + name_prefix = circuit_cell_name_prefix; } if (name_prefix) { @@ -413,7 +484,7 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & db::cell_index_type target_ci = target.add_cell ((std::string (name_prefix) + cell_name).c_str ()); cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), target_ci)).first; - build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, cell_name_prefix, device_cell_name_prefix, cmap); + build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, circuit_cell_name_prefix, device_cell_name_prefix, cmap, db::ICplxTrans ()); } else { cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), std::numeric_limits::max ())).first; @@ -422,7 +493,7 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & } if (cm->second != std::numeric_limits::max ()) { - target_cell.insert (db::CellInstArray (db::CellInst (cm->second), c->inst ().complex_trans ())); + target_cell->insert (db::CellInstArray (db::CellInst (cm->second), tr * c->inst ().complex_trans ())); } } @@ -437,7 +508,7 @@ LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &ta std::map, db::cell_index_type> cell_map; - build_net_rec (net, target, target_cell, lmap, cell_name_prefix, device_cell_name_prefix, cell_map); + build_net_rec (net, target, target_cell, lmap, 0, cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans ()); } void @@ -456,39 +527,55 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target continue; } + bool is_top_circuit = c->begin_parents () == c->end_parents (); + db::cell_index_type target_ci = cmap.cell_mapping (c->cell_index ()); for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { // exlude local nets in recursive mode - if (circuit_cell_name_prefix && n->pin_count () > 0) { + if (circuit_cell_name_prefix && ! is_top_circuit && n->pin_count () > 0) { continue; } - const db::connected_clusters &ccl = m_net_clusters.clusters_per_cell (c->cell_index ()); - const db::local_cluster &cl = ccl.cluster_by_id (n->cluster_id ()); + build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans ()); - bool any_connections = ! ccl.connections_for_cluster (n->cluster_id ()).empty (); + } - bool any_shapes = false; - for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any_shapes; ++m) { - any_shapes = ! cl.begin (layer_of (*m->second)).at_end (); - } + if (circuit_cell_name_prefix) { - if (any_shapes || (circuit_cell_name_prefix && any_connections)) { + // with recursive nets we skip nets in subcircuits which are connected upwards. This means, nets will + // get lost if there is no connection to this pin from the outside. Hence we need to deliver nets from + // subcircuits as part of the circuit which calls the subcircuit - but NOT in a subcircuit cell, because + // this will just apply to nets from certain instances. But the net cell name will be formed as "subcircuit:net" - db::cell_index_type net_ci = target_ci; + const db::Circuit &circuit = *c; + for (db::Circuit::const_subcircuit_iterator sc = circuit.begin_subcircuits (); sc != circuit.end_subcircuits (); ++sc) { - if (net_cell_name_prefix) { + const db::SubCircuit &subcircuit = *sc; + for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) { - db::Cell &tc = target.cell (target_ci); - net_ci = target.add_cell ((std::string (net_cell_name_prefix) + n->expanded_name ()).c_str ()); - tc.insert (db::CellInstArray (db::CellInst (net_ci), db::Trans ())); + if (! subcircuit.net_for_pin (p->id ())) { + + const db::Net *n = subcircuit.circuit_ref ()->net_for_pin (p->id ()); + if (n) { + + double dbu = target.dbu (); + db::ICplxTrans tr = db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu); + + if (net_cell_name_prefix) { + std::string ncn = std::string (net_cell_name_prefix) + subcircuit.expanded_name () + ":"; + build_net_rec (*n, target, target.cell (target_ci), lmap, ncn.c_str (), circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr); + } else { + build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr); + } + + } + + } } - build_net_rec (*n, target, target.cell (net_ci), lmap, circuit_cell_name_prefix, device_cell_name_prefix, cell_map); - } } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 7a0be2655..c332d48a0 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -141,7 +141,6 @@ public: /** * @brief Creates a new region representing an original layer * "layer_index" is the layer index of the desired layer in the original layout. - * The Region object returned is a new object and must be deleted by the caller. * This variant produces polygons and takes texts for net name annotation. * A variant not taking texts is "make_polygon_layer". A Variant only taking * texts is "make_text_layer". @@ -211,6 +210,13 @@ public: */ void extract_netlist (); + /** + * @brief Marks the netlist as extracted + * NOTE: this method is provided for special cases such as netlist readers. Don't + * use it. + */ + void set_netlist_extracted (); + /** * @brief Gets the internal layout */ @@ -268,6 +274,8 @@ public: /** * @brief gets the netlist extracted or make on if none exists yet. + * NOTE: this method is provided for special cases like readers of persisted + * layout to netlist data. */ db::Netlist *make_netlist (); @@ -401,8 +409,8 @@ private: bool m_netlist_extracted; size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path); - void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap) const; - void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap) const; + void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; + void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const Net *net, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; }; } diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index 8d0a6e894..fd6793473 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -110,6 +110,8 @@ namespace l2n_std_format param_key, location_key, rotation_key, mirror_key, scale_key, pin_key, indent1, indent2; + + inline static bool is_short () { return Short; } }; } diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index fe3a3ac5b..cc051af0b 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -51,6 +51,11 @@ public: m_layers.insert (std::make_pair (name, region)); } + bool has_layer (const std::string &name) const + { + return m_layers.find (name) != m_layers.end (); + } + db::Region &layer (const std::string &name) { std::map::const_iterator l = m_layers.find (name); @@ -180,6 +185,11 @@ void LayoutToNetlistStandardReader::read (db::LayoutToNetlist *l2n) } } +const db::Region *LayoutToNetlistStandardReader::layer_by_name (const std::string &name) +{ + return mp_layers->has_layer (name) ? &mp_layers->layer (name) : 0; +} + void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) { int version = 0; @@ -191,7 +201,7 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) l2n->make_netlist (); - Layers layers; + mp_layers.reset (new Layers ()); while (! at_end ()) { @@ -227,7 +237,7 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) Brace br (this); std::string layer; read_word_or_quoted (layer); - layers.add (layer, l2n->make_layer (layer)); + mp_layers->add (layer, l2n->make_layer (layer)); br.done (); } else if (test (skeys::connect_key) || test (lkeys::connect_key)) { @@ -238,7 +248,7 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) while (br) { std::string l2; read_word_or_quoted (l2); - l2n->connect (layers.layer (l1), layers.layer (l2)); + l2n->connect (mp_layers->layer (l1), mp_layers->layer (l2)); } br.done (); @@ -250,7 +260,7 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) while (br) { std::string g; read_word_or_quoted (g); - l2n->connect_global (layers.layer (l1), g); + l2n->connect_global (mp_layers->layer (l1), g); } br.done (); @@ -265,7 +275,8 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) l2n->netlist ()->add_circuit (circuit); db::Layout *ly = l2n->internal_layout (); - db::cell_index_type ci = ly->add_cell (name.c_str ()); + std::pair ci_old = ly->cell_by_name (name.c_str ()); + db::cell_index_type ci = ci_old.first ? ci_old.second : ly->add_cell (name.c_str ()); circuit->set_cell_index (ci); std::map > connections; @@ -273,7 +284,7 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) while (br) { if (test (skeys::net_key) || test (lkeys::net_key)) { - read_net (l2n, circuit, layers); + read_net (l2n, circuit); } else if (test (skeys::pin_key) || test (lkeys::pin_key)) { read_pin (l2n, circuit); } else if (test (skeys::device_key) || test (lkeys::device_key)) { @@ -340,7 +351,7 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) while (br) { if (test (skeys::terminal_key) || test (lkeys::terminal_key)) { - read_abstract_terminal (l2n, dm, gen_dc ? dc : 0, layers); + read_abstract_terminal (l2n, dm, gen_dc ? dc : 0); } else { throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device abstract definition (terminal expected)"))); } @@ -352,10 +363,12 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) } } + + l2n->set_netlist_extracted (); } std::pair -LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n, Layers &layers) +LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n) { std::string lname; @@ -364,7 +377,7 @@ LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n, Layers & Brace br (this); read_word_or_quoted (lname); - unsigned int lid = l2n->layer_of (layers.layer (lname)); + unsigned int lid = l2n->layer_of (mp_layers->layer (lname)); db::Coord l = read_coord (); db::Coord b = read_coord (); @@ -381,7 +394,7 @@ LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n, Layers & Brace br (this); read_word_or_quoted (lname); - unsigned int lid = l2n->layer_of (layers.layer (lname)); + unsigned int lid = l2n->layer_of (mp_layers->layer (lname)); std::vector pt; @@ -403,7 +416,7 @@ LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n, Layers & } void -LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, Layers &layers) +LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit) { Brace br (this); @@ -421,7 +434,7 @@ LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit * db::Cell &cell = l2n->internal_layout ()->cell (circuit->cell_index ()); while (br) { - std::pair pr = read_geometry (l2n, layers); + std::pair pr = read_geometry (l2n); lc.add (pr.second, pr.first); cell.shapes (pr.first).insert (pr.second); } @@ -675,7 +688,7 @@ LayoutToNetlistStandardReader::read_subcircuit (db::LayoutToNetlist *l2n, db::Ci } void -LayoutToNetlistStandardReader::read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceModel *dm, db::DeviceClass *dc, Layers &layers) +LayoutToNetlistStandardReader::read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceModel *dm, db::DeviceClass *dc) { Brace br (this); @@ -707,7 +720,7 @@ LayoutToNetlistStandardReader::read_abstract_terminal (db::LayoutToNetlist *l2n, db::Cell &cell = l2n->internal_layout ()->cell (dm->cell_index ()); while (br) { - std::pair pr = read_geometry (l2n, layers); + std::pair pr = read_geometry (l2n); lc.add (pr.second, pr.first); cell.shapes (pr.first).insert (pr.second); } diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h index 6fe9a566a..276614197 100644 --- a/src/db/db/dbLayoutToNetlistReader.h +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -40,6 +40,7 @@ class Circuit; class Cell; class DeviceModel; class DeviceClass; +class Region; /** * @brief The base class for a LayoutToNetlist writer @@ -64,6 +65,8 @@ public: void read (db::LayoutToNetlist *l2n); + const db::Region *layer_by_name (const std::string &name); + private: friend class l2n_std_reader::Brace; typedef l2n_std_reader::Brace Brace; @@ -82,6 +85,7 @@ private: std::string m_path; std::string m_line; tl::Extractor m_ex; + std::auto_ptr mp_layers; void do_read (db::LayoutToNetlist *l2n); @@ -94,12 +98,12 @@ private: bool at_end (); void skip (); - void read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, Layers &layers); + void read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit); void read_pin (db::LayoutToNetlist *l2n, db::Circuit *circuit); db::CellInstArray read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs); db::CellInstArray read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs); - void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceModel *dm, db::DeviceClass *dc, Layers &layers); - std::pair read_geometry (db::LayoutToNetlist *l2n, Layers &layers); + void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceModel *dm, db::DeviceClass *dc); + std::pair read_geometry (db::LayoutToNetlist *l2n); }; } diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 2daa621be..437ca16d8 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -83,23 +83,33 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) const db::Layout *ly = l2n->internal_layout (); const db::Netlist *nl = l2n->netlist (); - *mp_stream << "# General section" << endl; - *mp_stream << "# Lists general definitions." << endl << endl; + *mp_stream << "#%l2n-klayout" << endl; + + if (! Keys::is_short ()) { + *mp_stream << endl << "# General section" << endl << endl; + } + if (version > 0) { *mp_stream << Keys::version_key << "(" << version << ")" << endl; } *mp_stream << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; *mp_stream << Keys::unit_key << "(" << ly->dbu () << ")" << endl; - *mp_stream << endl << "# Layer section" << endl; - *mp_stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl; + if (! Keys::is_short ()) { + *mp_stream << endl << "# Layer section" << endl; + *mp_stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl; + } - *mp_stream << endl << "# Mask layers" << endl; + if (! Keys::is_short ()) { + *mp_stream << endl << "# Mask layers" << endl; + } for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { *mp_stream << Keys::layer_key << "(" << name_for_layer (ly, *l) << ")" << endl; } - *mp_stream << endl << "# Mask layer connectivity" << endl; + if (! Keys::is_short ()) { + *mp_stream << endl << "# Mask layer connectivity" << endl; + } for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l); @@ -121,7 +131,9 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) db::Connectivity::global_nets_iterator gb = l2n->connectivity ().begin_global_connections (*l); if (gb != ge) { if (! any) { - *mp_stream << endl << "# Global nets and connectivity" << endl; + if (! Keys::is_short ()) { + *mp_stream << endl << "# Global nets and connectivity" << endl; + } any = true; } *mp_stream << Keys::global_key << "(" << name_for_layer (ly, *l); @@ -133,7 +145,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) } - if (nl->begin_device_models () != nl->end_device_models ()) { + if (nl->begin_device_models () != nl->end_device_models () && ! Keys::is_short ()) { *mp_stream << endl << "# Device abstracts section" << endl; *mp_stream << "# Device abstracts list the pin shapes of the devices." << endl; } @@ -145,8 +157,10 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) } } - *mp_stream << endl << "# Circuit section" << endl; - *mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl; + if (! Keys::is_short ()) { + *mp_stream << endl << "# Circuit section" << endl; + *mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl; + } for (db::Netlist::const_bottom_up_circuit_iterator i = nl->begin_bottom_up (); i != nl->end_bottom_up (); ++i) { const db::Circuit *x = *i; *mp_stream << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; @@ -159,14 +173,18 @@ template void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit) { if (circuit.begin_nets () != circuit.end_nets ()) { - *mp_stream << endl << indent1 << "# Nets with their geometries" << endl; + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Nets with their geometries" << endl; + } for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { write (l2n, *n); } } if (circuit.begin_pins () != circuit.end_pins ()) { - *mp_stream << endl << indent1 << "# Outgoing pins and their connections to nets" << endl; + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Outgoing pins and their connections to nets" << endl; + } for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { const db::Net *net = circuit.net_for_pin (p->id ()); if (net) { @@ -176,20 +194,26 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Cir } if (circuit.begin_devices () != circuit.end_devices ()) { - *mp_stream << endl << indent1 << "# Devices and their connections" << endl; + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Devices and their connections" << endl; + } for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { write (l2n, *d); } } if (circuit.begin_subcircuits () != circuit.end_subcircuits ()) { - *mp_stream << endl << indent1 << "# Subcircuits and their connections" << endl; + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Subcircuits and their connections" << endl; + } for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) { write (l2n, *x); } } - *mp_stream << endl; + if (! Keys::is_short ()) { + *mp_stream << endl; + } } template diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc index 8aa43816a..549466153 100644 --- a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -26,6 +26,7 @@ #include "dbStream.h" #include "dbCommonReader.h" #include "dbNetlistDeviceExtractorClasses.h" +#include "dbTestSupport.h" #include "tlUnitTest.h" #include "tlStream.h" @@ -64,17 +65,59 @@ TEST(1_ReaderBasic) tl::absolute_file_path (au_path))); } + // test build_all_nets from read l2n -#if 0 + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - std::string path = tmp_file ("tmp_l2nwriter_1.txt"); + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = reader.layer_by_name ("psd"); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = reader.layer_by_name ("nsd"); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = reader.layer_by_name ("poly"); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = reader.layer_by_name ("diff_cont"); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = reader.layer_by_name ("poly_cont"); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = reader.layer_by_name ("metal1"); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = reader.layer_by_name ("via1"); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = reader.layer_by_name ("metal2"); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, "DEVICE_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "l2n_writer_au.gds"); + + db::compare_layouts (_this, ly2, au); + } +} + +TEST(2_ReaderBasicShort) +{ + db::Layout ly; + + db::Cell &tc = ly.cell (ly.add_cell ("TOP")); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_s.txt"); + tl::InputStream is_in (in_path); + + db::LayoutToNetlistStandardReader reader (is_in); + reader.read (&l2n); + + // verify against the input + + std::string path = tmp_file ("tmp_l2nreader_2.txt"); { tl::OutputStream stream (path); - db::LayoutToNetlistStandardWriter writer (stream, false); + db::LayoutToNetlistStandardWriter writer (stream, true); writer.write (&l2n); } - std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt"); + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_s.txt"); tl::InputStream is (path); tl::InputStream is_au (au_path); @@ -85,5 +128,4 @@ TEST(1_ReaderBasic) tl::absolute_file_path (au_path))); } -#endif } diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 195f9b2c2..54f6f4b60 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -25,6 +25,7 @@ #include "dbStream.h" #include "dbCommonReader.h" #include "dbNetlistDeviceExtractorClasses.h" +#include "dbTestSupport.h" #include "tlUnitTest.h" #include "tlStream.h" @@ -163,6 +164,7 @@ TEST(1_WriterBasic) rpoly_lbl.reset (0); l2n.extract_netlist (); + l2n.netlist ()->make_top_level_pins (); l2n.netlist ()->purge (); std::string path = tmp_file ("tmp_l2nwriter_1.txt"); @@ -174,12 +176,289 @@ TEST(1_WriterBasic) std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt"); - tl::InputStream is (path); - tl::InputStream is_au (au_path); + { + tl::InputStream is (path); + tl::InputStream is_au (au_path); - if (is.read_all () != is_au.read_all ()) { - _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", - tl::absolute_file_path (path), - tl::absolute_file_path (au_path))); + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + } + + path = tmp_file ("tmp_l2nwriter_1s.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, true); + writer.write (&l2n); + } + + au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_s.txt"); + + { + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + } + + // test build_all_nets (verify reference for reader) + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, "DEVICE_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "l2n_writer_au.gds"); + + db::compare_layouts (_this, ly2, au); + } +} + +TEST(2_WriterWithGlobalNets) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int pplus = define_layer (ly, lmap, 10); + unsigned int nplus = define_layer (ly, lmap, 11); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l3.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rbulk (l2n.make_layer (ly.insert_layer (), "rbulk")); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell & *rpplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + l2n.name (rpactive, "pactive"); + l2n.name (rntie, "ntie"); + l2n.name (rpgate, "pgate"); + l2n.name (rpsd, "psd"); + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell & *rnplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + l2n.name (rnactive, "nactive"); + l2n.name (rptie, "ptie"); + l2n.name (rngate, "ngate"); + l2n.name (rnsd, "nsd"); + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion + unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie + unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpsd.insert_into (&ly, tc.cell_index (), lptie); + rnsd.insert_into (&ly, tc.cell_index (), lntie); + + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rdiff_cont, rptie); + l2n.connect (*rdiff_cont, rntie); + l2n.connect (*rnwell, rntie); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + l2n.connect_global (*rbulk, "BULK"); + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + std::string path = tmp_file ("tmp_l2nwriter_2.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2.txt"); + + { + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + } + + path = tmp_file ("tmp_l2nwriter_2s.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, true); + writer.write (&l2n); + } + + au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2s.txt"); + + { + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + } + + // test build_all_nets + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (12, 0))] = rbulk.get (); + lmap [ly2.insert_layer (db::LayerProperties (13, 0))] = &rptie; + lmap [ly2.insert_layer (db::LayerProperties (14, 0))] = &rntie; + lmap [ly2.insert_layer (db::LayerProperties (1, 0)) ] = rnwell.get (); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "l2n_writer_au_2.gds"); + + db::compare_layouts (_this, ly2, au); } } diff --git a/testdata/algo/l2n_writer_au.gds b/testdata/algo/l2n_writer_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..ff863024361e56b4d9fe2071a86c1618c4049263 GIT binary patch literal 18548 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/l2n_writer_au_2.txt b/testdata/algo/l2n_writer_au_2.txt new file mode 100644 index 000000000..f4e5c05c7 --- /dev/null +++ b/testdata/algo/l2n_writer_au_2.txt @@ -0,0 +1,566 @@ +#%l2n-klayout + +# General section + +top(RINGO) +unit(0.001) + +# Layer section +# This section lists the mask layers (drawing or derived) and their connections. + +# Mask layers +layer(rbulk) +layer(nwell) +layer(poly) +layer(poly_lbl) +layer(diff_cont) +layer(poly_cont) +layer(metal1) +layer(metal1_lbl) +layer(via1) +layer(metal2) +layer(metal2_lbl) +layer(ntie) +layer(psd) +layer(ptie) +layer(nsd) + +# Mask layer connectivity +connect(nwell nwell ntie) +connect(poly poly poly_lbl poly_cont) +connect(poly_lbl poly) +connect(diff_cont diff_cont metal1 ntie psd ptie nsd) +connect(poly_cont poly poly_cont metal1) +connect(metal1 diff_cont poly_cont metal1 metal1_lbl via1) +connect(metal1_lbl metal1) +connect(via1 metal1 via1 metal2) +connect(metal2 via1 metal2 metal2_lbl) +connect(metal2_lbl metal2) +connect(ntie nwell diff_cont ntie) +connect(psd diff_cont psd) +connect(ptie diff_cont ptie) +connect(nsd diff_cont nsd) + +# Global nets and connectivity +global(rbulk BULK) +global(ptie BULK) + +# Device abstracts section +# Device abstracts list the pin shapes of the devices. +device(D$PMOS PMOS + terminal(S + rect(psd -650 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(psd 125 -475 675 475) + ) + terminal(B + rect(nwell -125 -475 125 475) + ) +) +device(D$PMOS$1 PMOS + terminal(S + rect(psd -675 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(psd 125 -475 650 475) + ) + terminal(B + rect(nwell -125 -475 125 475) + ) +) +device(D$NMOS NMOS + terminal(S + rect(nsd -650 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(nsd 125 -475 675 475) + ) + terminal(B + rect(rbulk -125 -475 125 475) + ) +) +device(D$NMOS$1 NMOS + terminal(S + rect(nsd -675 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(nsd 125 -475 650 475) + ) + terminal(B + rect(rbulk -125 -475 125 475) + ) +) + +# Circuit section +# Circuits are the hierarchical building blocks of the netlist. +circuit(INV2 + + # Nets with their geometries + net($1 + rect(nwell -1400 1800 1400 4580) + rect(diff_cont -110 3930 110 4150) + rect(ntie -400 3700 400 4380) + ) + net(IN + rect(poly -525 -250 -275 2250) + rect(poly -1700 1620 -400 1980) + rect(poly -525 -800 -275 800) + rect(poly -525 2000 -275 3600) + rect(poly_lbl -801 1799 -799 1801) + rect(poly_cont -1630 1690 -1410 1910) + ) + net($3 + rect(poly 275 -250 525 2250) + rect(poly 220 820 580 1180) + rect(poly 275 2000 525 3600) + rect(poly 275 -800 525 800) + rect(diff_cont -910 2490 -690 2710) + rect(diff_cont -910 2890 -690 3110) + rect(diff_cont -910 -310 -690 -90) + rect(diff_cont -910 90 -690 310) + rect(poly_cont 290 890 510 1110) + rect(metal1 -800 820 580 1180) + rect(metal1 -980 -420 -620 2420) + rect(metal1 -980 2420 -620 3180) + rect(metal1 -980 -380 -620 380) + rect(psd -1050 2325 -525 3275) + rect(nsd -1050 -475 -525 475) + ) + net(OUT + rect(diff_cont 690 2890 910 3110) + rect(diff_cont 690 2490 910 2710) + rect(diff_cont 690 90 910 310) + rect(diff_cont 690 -310 910 -90) + polygon(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20) + rect(metal1 620 2420 980 3180) + rect(metal1 620 -380 980 380) + rect(metal1_lbl 799 1799 801 1801) + rect(psd 525 2325 1050 3275) + rect(nsd 525 -475 1050 475) + ) + net(VSS + rect(diff_cont -110 -310 110 -90) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 -310 110 -90) + rect(metal1 -180 -380 180 380) + rect(metal1 -180 -380 180 380) + rect(via1 -125 -325 125 -75) + rect(via1 -125 75 125 325) + rect(metal2 -1400 -450 1400 450) + rect(metal2_lbl 1239 -91 1241 -89) + rect(nsd -275 -475 275 475) + ) + net(VDD + rect(diff_cont -110 2490 110 2710) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2490 110 2710) + rect(metal1 -180 2420 180 3180) + rect(metal1 -180 2420 180 3180) + rect(via1 -125 2475 125 2725) + rect(via1 -125 2875 125 3125) + rect(metal2 -1400 2350 1400 3250) + rect(metal2_lbl 1249 2799 1251 2801) + rect(psd -275 2325 275 3275) + ) + net(BULK + rect(diff_cont -110 -1360 110 -1140) + rect(ptie -400 -1590 400 -910) + ) + + # Outgoing pins and their connections to nets + pin($0 $1) + pin(IN IN) + pin($2 $3) + pin(OUT OUT) + pin(VSS VSS) + pin(VDD VDD) + pin(BULK BULK) + + # Devices and their connections + device($1 D$PMOS + location(-400 2800) + param(L 0.25) + param(W 0.95) + param(AS 0.49875) + param(AD 0.26125) + terminal(S $3) + terminal(G IN) + terminal(D VDD) + terminal(B $1) + ) + device($2 D$PMOS$1 + location(400 2800) + param(L 0.25) + param(W 0.95) + param(AS 0.26125) + param(AD 0.49875) + terminal(S VDD) + terminal(G $3) + terminal(D OUT) + terminal(B $1) + ) + device($3 D$NMOS + location(-400 0) + param(L 0.25) + param(W 0.95) + param(AS 0.49875) + param(AD 0.26125) + terminal(S $3) + terminal(G IN) + terminal(D VSS) + terminal(B BULK) + ) + device($4 D$NMOS$1 + location(400 0) + param(L 0.25) + param(W 0.95) + param(AS 0.26125) + param(AD 0.49875) + terminal(S VSS) + terminal(G $3) + terminal(D OUT) + terminal(B BULK) + ) + +) +circuit(INV2PAIR + + # Nets with their geometries + net(BULK) + net($I8 + rect(diff_cont 3430 3290 3650 3510) + rect(diff_cont 3430 3690 3650 3910) + rect(diff_cont 3430 490 3650 710) + rect(diff_cont 3430 890 3650 1110) + ) + net($I6 + rect(diff_cont 4230 3290 4450 3510) + rect(diff_cont 4230 3690 4450 3910) + rect(diff_cont 4230 3690 4450 3910) + rect(diff_cont 4230 3290 4450 3510) + rect(diff_cont 1590 3290 1810 3510) + rect(diff_cont 1590 3690 1810 3910) + rect(diff_cont 1590 3690 1810 3910) + rect(diff_cont 1590 3290 1810 3510) + rect(metal1 4160 3220 4520 3980) + rect(metal1 4160 3220 4520 3980) + rect(metal1 1520 3220 1880 3980) + rect(metal1 1520 3220 1880 3980) + ) + net($I5 + rect(diff_cont 4230 490 4450 710) + rect(diff_cont 4230 890 4450 1110) + rect(diff_cont 4230 890 4450 1110) + rect(diff_cont 4230 490 4450 710) + rect(diff_cont 1590 490 1810 710) + rect(diff_cont 1590 890 1810 1110) + rect(diff_cont 1590 890 1810 1110) + rect(diff_cont 1590 490 1810 710) + rect(metal1 4160 420 4520 1180) + rect(metal1 4160 420 4520 1180) + rect(metal1 1520 420 1880 1180) + rect(metal1 1520 420 1880 1180) + ) + net($I4 + rect(diff_cont 2390 3690 2610 3910) + rect(diff_cont 2390 3290 2610 3510) + rect(diff_cont 2390 890 2610 1110) + rect(diff_cont 2390 490 2610 710) + ) + net($I3) + net($I2 + rect(diff_cont 5030 3690 5250 3910) + rect(diff_cont 5030 3290 5250 3510) + rect(diff_cont 5030 890 5250 1110) + rect(diff_cont 5030 490 5250 710) + ) + net($I1) + + # Outgoing pins and their connections to nets + pin(BULK BULK) + pin($1 $I8) + pin($2 $I6) + pin($3 $I5) + pin($4 $I3) + pin($5 $I2) + pin($6 $I1) + + # Subcircuits and their connections + circuit($1 INV2 location(1700 800) + pin($0 $I1) + pin(IN $I3) + pin(OUT $I4) + pin(VSS $I5) + pin(VDD $I6) + pin(BULK BULK) + ) + circuit($2 INV2 location(4340 800) + pin($0 $I1) + pin(IN $I4) + pin($2 $I8) + pin(OUT $I2) + pin(VSS $I5) + pin(VDD $I6) + pin(BULK BULK) + ) + +) +circuit(RINGO + + # Nets with their geometries + net(FB + rect(diff_cont 22850 2490 23070 2710) + rect(diff_cont 22850 2890 23070 3110) + rect(diff_cont 22850 -310 23070 -90) + rect(diff_cont 22850 90 23070 310) + rect(metal1 -1700 1620 -1340 1980) + rect(via1 -1645 1675 -1395 1925) + rect(via1 22835 1675 23085 1925) + rect(metal2 -1720 1600 23160 2000) + rect(metal2_lbl -1 1799 1 1801) + ) + net(OSC + rect(diff_cont 24450 2890 24670 3110) + rect(diff_cont 24450 2490 24670 2710) + rect(diff_cont 24450 90 24670 310) + rect(diff_cont 24450 -310 24670 -90) + rect(via1 24435 1675 24685 1925) + rect(metal2 24360 1600 24760 2000) + rect(metal2_lbl 24559 1799 24561 1801) + ) + net(VDD + rect(diff_cont 7810 2490 8030 2710) + rect(diff_cont 7810 2890 8030 3110) + rect(diff_cont 7810 2890 8030 3110) + rect(diff_cont 7810 2490 8030 2710) + rect(diff_cont 5170 2490 5390 2710) + rect(diff_cont 5170 2890 5390 3110) + rect(diff_cont 5170 2890 5390 3110) + rect(diff_cont 5170 2490 5390 2710) + rect(diff_cont 2530 2490 2750 2710) + rect(diff_cont 2530 2890 2750 3110) + rect(diff_cont 2530 2890 2750 3110) + rect(diff_cont 2530 2490 2750 2710) + rect(diff_cont -110 2490 110 2710) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2490 110 2710) + rect(diff_cont 13090 2490 13310 2710) + rect(diff_cont 13090 2890 13310 3110) + rect(diff_cont 13090 2890 13310 3110) + rect(diff_cont 13090 2490 13310 2710) + rect(diff_cont 10450 2490 10670 2710) + rect(diff_cont 10450 2890 10670 3110) + rect(diff_cont 10450 2890 10670 3110) + rect(diff_cont 10450 2490 10670 2710) + rect(diff_cont 18370 2490 18590 2710) + rect(diff_cont 18370 2890 18590 3110) + rect(diff_cont 18370 2890 18590 3110) + rect(diff_cont 18370 2490 18590 2710) + rect(diff_cont 15730 2490 15950 2710) + rect(diff_cont 15730 2890 15950 3110) + rect(diff_cont 15730 2890 15950 3110) + rect(diff_cont 15730 2490 15950 2710) + rect(diff_cont 23650 2490 23870 2710) + rect(diff_cont 23650 2890 23870 3110) + rect(diff_cont 23650 2890 23870 3110) + rect(diff_cont 23650 2490 23870 2710) + rect(diff_cont 21010 2490 21230 2710) + rect(diff_cont 21010 2890 21230 3110) + rect(diff_cont 21010 2890 21230 3110) + rect(diff_cont 21010 2490 21230 2710) + rect(metal1 -180 3100 180 4220) + rect(metal1 2460 3100 2820 4220) + rect(metal1 5100 3100 5460 4220) + rect(metal1 7740 3100 8100 4220) + rect(metal1 10380 3100 10740 4220) + rect(metal1 13020 3100 13380 4220) + rect(metal1 15660 3100 16020 4220) + rect(metal1 18300 3100 18660 4220) + rect(metal1 20940 3100 21300 4220) + rect(metal1 23580 3100 23940 4220) + rect(metal1 7740 2420 8100 3180) + rect(metal1 7740 2420 8100 3180) + rect(metal1 5100 2420 5460 3180) + rect(metal1 5100 2420 5460 3180) + rect(metal1 2460 2420 2820 3180) + rect(metal1 2460 2420 2820 3180) + rect(metal1 -180 2420 180 3180) + rect(metal1 -180 2420 180 3180) + rect(metal1 13020 2420 13380 3180) + rect(metal1 13020 2420 13380 3180) + rect(metal1 10380 2420 10740 3180) + rect(metal1 10380 2420 10740 3180) + rect(metal1 18300 2420 18660 3180) + rect(metal1 18300 2420 18660 3180) + rect(metal1 15660 2420 16020 3180) + rect(metal1 15660 2420 16020 3180) + rect(metal1 23580 2420 23940 3180) + rect(metal1 23580 2420 23940 3180) + rect(metal1 20940 2420 21300 3180) + rect(metal1 20940 2420 21300 3180) + rect(metal2_lbl -1 2799 1 2801) + ) + net('BULK,VSS' + rect(diff_cont 7810 -310 8030 -90) + rect(diff_cont 7810 90 8030 310) + rect(diff_cont 7810 90 8030 310) + rect(diff_cont 7810 -310 8030 -90) + rect(diff_cont 5170 -310 5390 -90) + rect(diff_cont 5170 90 5390 310) + rect(diff_cont 5170 90 5390 310) + rect(diff_cont 5170 -310 5390 -90) + rect(diff_cont 2530 -310 2750 -90) + rect(diff_cont 2530 90 2750 310) + rect(diff_cont 2530 90 2750 310) + rect(diff_cont 2530 -310 2750 -90) + rect(diff_cont -110 -310 110 -90) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 -310 110 -90) + rect(diff_cont 13090 -310 13310 -90) + rect(diff_cont 13090 90 13310 310) + rect(diff_cont 13090 90 13310 310) + rect(diff_cont 13090 -310 13310 -90) + rect(diff_cont 10450 -310 10670 -90) + rect(diff_cont 10450 90 10670 310) + rect(diff_cont 10450 90 10670 310) + rect(diff_cont 10450 -310 10670 -90) + rect(diff_cont 18370 -310 18590 -90) + rect(diff_cont 18370 90 18590 310) + rect(diff_cont 18370 90 18590 310) + rect(diff_cont 18370 -310 18590 -90) + rect(diff_cont 15730 -310 15950 -90) + rect(diff_cont 15730 90 15950 310) + rect(diff_cont 15730 90 15950 310) + rect(diff_cont 15730 -310 15950 -90) + rect(diff_cont 23650 -310 23870 -90) + rect(diff_cont 23650 90 23870 310) + rect(diff_cont 23650 90 23870 310) + rect(diff_cont 23650 -310 23870 -90) + rect(diff_cont 21010 -310 21230 -90) + rect(diff_cont 21010 90 21230 310) + rect(diff_cont 21010 90 21230 310) + rect(diff_cont 21010 -310 21230 -90) + rect(metal1 -180 -1420 180 -300) + rect(metal1 2460 -1420 2820 -300) + rect(metal1 5100 -1420 5460 -300) + rect(metal1 7740 -1420 8100 -300) + rect(metal1 10380 -1420 10740 -300) + rect(metal1 13020 -1420 13380 -300) + rect(metal1 15660 -1420 16020 -300) + rect(metal1 18300 -1420 18660 -300) + rect(metal1 20940 -1420 21300 -300) + rect(metal1 23580 -1420 23940 -300) + rect(metal1 7740 -380 8100 380) + rect(metal1 7740 -380 8100 380) + rect(metal1 5100 -380 5460 380) + rect(metal1 5100 -380 5460 380) + rect(metal1 2460 -380 2820 380) + rect(metal1 2460 -380 2820 380) + rect(metal1 -180 -380 180 380) + rect(metal1 -180 -380 180 380) + rect(metal1 13020 -380 13380 380) + rect(metal1 13020 -380 13380 380) + rect(metal1 10380 -380 10740 380) + rect(metal1 10380 -380 10740 380) + rect(metal1 18300 -380 18660 380) + rect(metal1 18300 -380 18660 380) + rect(metal1 15660 -380 16020 380) + rect(metal1 15660 -380 16020 380) + rect(metal1 23580 -380 23940 380) + rect(metal1 23580 -380 23940 380) + rect(metal1 20940 -380 21300 380) + rect(metal1 20940 -380 21300 380) + rect(metal2_lbl -1 -1 1 1) + ) + net($I13 + rect(diff_cont 3330 2890 3550 3110) + rect(diff_cont 3330 2490 3550 2710) + rect(diff_cont 3330 90 3550 310) + rect(diff_cont 3330 -310 3550 -90) + ) + net($I7 + rect(diff_cont 19170 2890 19390 3110) + rect(diff_cont 19170 2490 19390 2710) + rect(diff_cont 19170 90 19390 310) + rect(diff_cont 19170 -310 19390 -90) + ) + net($I6 + rect(diff_cont 13890 2890 14110 3110) + rect(diff_cont 13890 2490 14110 2710) + rect(diff_cont 13890 90 14110 310) + rect(diff_cont 13890 -310 14110 -90) + ) + net($I5 + rect(diff_cont 8610 2890 8830 3110) + rect(diff_cont 8610 2490 8830 2710) + rect(diff_cont 8610 90 8830 310) + rect(diff_cont 8610 -310 8830 -90) + ) + + # Outgoing pins and their connections to nets + pin(FB FB) + pin(OSC OSC) + pin(VDD VDD) + pin('BULK,VSS' 'BULK,VSS') + + # Subcircuits and their connections + circuit($1 INV2PAIR location(19420 -800) + pin(BULK 'BULK,VSS') + pin($1 FB) + pin($2 VDD) + pin($3 'BULK,VSS') + pin($4 $I7) + pin($5 OSC) + pin($6 VDD) + ) + circuit($2 INV2PAIR location(-1700 -800) + pin(BULK 'BULK,VSS') + pin($2 VDD) + pin($3 'BULK,VSS') + pin($4 FB) + pin($5 $I13) + pin($6 VDD) + ) + circuit($3 INV2PAIR location(3580 -800) + pin(BULK 'BULK,VSS') + pin($2 VDD) + pin($3 'BULK,VSS') + pin($4 $I13) + pin($5 $I5) + pin($6 VDD) + ) + circuit($4 INV2PAIR location(8860 -800) + pin(BULK 'BULK,VSS') + pin($2 VDD) + pin($3 'BULK,VSS') + pin($4 $I5) + pin($5 $I6) + pin($6 VDD) + ) + circuit($5 INV2PAIR location(14140 -800) + pin(BULK 'BULK,VSS') + pin($2 VDD) + pin($3 'BULK,VSS') + pin($4 $I6) + pin($5 $I7) + pin($6 VDD) + ) + +) diff --git a/testdata/algo/l2n_writer_au_2s.txt b/testdata/algo/l2n_writer_au_2s.txt new file mode 100644 index 000000000..2465db56d --- /dev/null +++ b/testdata/algo/l2n_writer_au_2s.txt @@ -0,0 +1,527 @@ +#%l2n-klayout +W(RINGO) +U(0.001) +L(rbulk) +L(nwell) +L(poly) +L(poly_lbl) +L(diff_cont) +L(poly_cont) +L(metal1) +L(metal1_lbl) +L(via1) +L(metal2) +L(metal2_lbl) +L(ntie) +L(psd) +L(ptie) +L(nsd) +C(nwell nwell ntie) +C(poly poly poly_lbl poly_cont) +C(poly_lbl poly) +C(diff_cont diff_cont metal1 ntie psd ptie nsd) +C(poly_cont poly poly_cont metal1) +C(metal1 diff_cont poly_cont metal1 metal1_lbl via1) +C(metal1_lbl metal1) +C(via1 metal1 via1 metal2) +C(metal2 via1 metal2 metal2_lbl) +C(metal2_lbl metal2) +C(ntie nwell diff_cont ntie) +C(psd diff_cont psd) +C(ptie diff_cont ptie) +C(nsd diff_cont nsd) +G(rbulk BULK) +G(ptie BULK) +D(D$PMOS PMOS + T(S + R(psd -650 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(psd 125 -475 675 475) + ) + T(B + R(nwell -125 -475 125 475) + ) +) +D(D$PMOS$1 PMOS + T(S + R(psd -675 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(psd 125 -475 650 475) + ) + T(B + R(nwell -125 -475 125 475) + ) +) +D(D$NMOS NMOS + T(S + R(nsd -650 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(nsd 125 -475 675 475) + ) + T(B + R(rbulk -125 -475 125 475) + ) +) +D(D$NMOS$1 NMOS + T(S + R(nsd -675 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(nsd 125 -475 650 475) + ) + T(B + R(rbulk -125 -475 125 475) + ) +) +X(INV2 + N($1 + R(nwell -1400 1800 1400 4580) + R(diff_cont -110 3930 110 4150) + R(ntie -400 3700 400 4380) + ) + N(IN + R(poly -525 -250 -275 2250) + R(poly -1700 1620 -400 1980) + R(poly -525 -800 -275 800) + R(poly -525 2000 -275 3600) + R(poly_lbl -801 1799 -799 1801) + R(poly_cont -1630 1690 -1410 1910) + ) + N($3 + R(poly 275 -250 525 2250) + R(poly 220 820 580 1180) + R(poly 275 2000 525 3600) + R(poly 275 -800 525 800) + R(diff_cont -910 2490 -690 2710) + R(diff_cont -910 2890 -690 3110) + R(diff_cont -910 -310 -690 -90) + R(diff_cont -910 90 -690 310) + R(poly_cont 290 890 510 1110) + R(metal1 -800 820 580 1180) + R(metal1 -980 -420 -620 2420) + R(metal1 -980 2420 -620 3180) + R(metal1 -980 -380 -620 380) + R(psd -1050 2325 -525 3275) + R(nsd -1050 -475 -525 475) + ) + N(OUT + R(diff_cont 690 2890 910 3110) + R(diff_cont 690 2490 910 2710) + R(diff_cont 690 90 910 310) + R(diff_cont 690 -310 910 -90) + Q(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20) + R(metal1 620 2420 980 3180) + R(metal1 620 -380 980 380) + R(metal1_lbl 799 1799 801 1801) + R(psd 525 2325 1050 3275) + R(nsd 525 -475 1050 475) + ) + N(VSS + R(diff_cont -110 -310 110 -90) + R(diff_cont -110 90 110 310) + R(diff_cont -110 90 110 310) + R(diff_cont -110 -310 110 -90) + R(metal1 -180 -380 180 380) + R(metal1 -180 -380 180 380) + R(via1 -125 -325 125 -75) + R(via1 -125 75 125 325) + R(metal2 -1400 -450 1400 450) + R(metal2_lbl 1239 -91 1241 -89) + R(nsd -275 -475 275 475) + ) + N(VDD + R(diff_cont -110 2490 110 2710) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2490 110 2710) + R(metal1 -180 2420 180 3180) + R(metal1 -180 2420 180 3180) + R(via1 -125 2475 125 2725) + R(via1 -125 2875 125 3125) + R(metal2 -1400 2350 1400 3250) + R(metal2_lbl 1249 2799 1251 2801) + R(psd -275 2325 275 3275) + ) + N(BULK + R(diff_cont -110 -1360 110 -1140) + R(ptie -400 -1590 400 -910) + ) + P($0 $1) + P(IN IN) + P($2 $3) + P(OUT OUT) + P(VSS VSS) + P(VDD VDD) + P(BULK BULK) + D($1 D$PMOS + Y(-400 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.49875) + E(AD 0.26125) + T(S $3) + T(G IN) + T(D VDD) + T(B $1) + ) + D($2 D$PMOS$1 + Y(400 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.26125) + E(AD 0.49875) + T(S VDD) + T(G $3) + T(D OUT) + T(B $1) + ) + D($3 D$NMOS + Y(-400 0) + E(L 0.25) + E(W 0.95) + E(AS 0.49875) + E(AD 0.26125) + T(S $3) + T(G IN) + T(D VSS) + T(B BULK) + ) + D($4 D$NMOS$1 + Y(400 0) + E(L 0.25) + E(W 0.95) + E(AS 0.26125) + E(AD 0.49875) + T(S VSS) + T(G $3) + T(D OUT) + T(B BULK) + ) +) +X(INV2PAIR + N(BULK) + N($I8 + R(diff_cont 3430 3290 3650 3510) + R(diff_cont 3430 3690 3650 3910) + R(diff_cont 3430 490 3650 710) + R(diff_cont 3430 890 3650 1110) + ) + N($I6 + R(diff_cont 4230 3290 4450 3510) + R(diff_cont 4230 3690 4450 3910) + R(diff_cont 4230 3690 4450 3910) + R(diff_cont 4230 3290 4450 3510) + R(diff_cont 1590 3290 1810 3510) + R(diff_cont 1590 3690 1810 3910) + R(diff_cont 1590 3690 1810 3910) + R(diff_cont 1590 3290 1810 3510) + R(metal1 4160 3220 4520 3980) + R(metal1 4160 3220 4520 3980) + R(metal1 1520 3220 1880 3980) + R(metal1 1520 3220 1880 3980) + ) + N($I5 + R(diff_cont 4230 490 4450 710) + R(diff_cont 4230 890 4450 1110) + R(diff_cont 4230 890 4450 1110) + R(diff_cont 4230 490 4450 710) + R(diff_cont 1590 490 1810 710) + R(diff_cont 1590 890 1810 1110) + R(diff_cont 1590 890 1810 1110) + R(diff_cont 1590 490 1810 710) + R(metal1 4160 420 4520 1180) + R(metal1 4160 420 4520 1180) + R(metal1 1520 420 1880 1180) + R(metal1 1520 420 1880 1180) + ) + N($I4 + R(diff_cont 2390 3690 2610 3910) + R(diff_cont 2390 3290 2610 3510) + R(diff_cont 2390 890 2610 1110) + R(diff_cont 2390 490 2610 710) + ) + N($I3) + N($I2 + R(diff_cont 5030 3690 5250 3910) + R(diff_cont 5030 3290 5250 3510) + R(diff_cont 5030 890 5250 1110) + R(diff_cont 5030 490 5250 710) + ) + N($I1) + P(BULK BULK) + P($1 $I8) + P($2 $I6) + P($3 $I5) + P($4 $I3) + P($5 $I2) + P($6 $I1) + X($1 INV2 Y(1700 800) + P($0 $I1) + P(IN $I3) + P(OUT $I4) + P(VSS $I5) + P(VDD $I6) + P(BULK BULK) + ) + X($2 INV2 Y(4340 800) + P($0 $I1) + P(IN $I4) + P($2 $I8) + P(OUT $I2) + P(VSS $I5) + P(VDD $I6) + P(BULK BULK) + ) +) +X(RINGO + N(FB + R(diff_cont 22850 2490 23070 2710) + R(diff_cont 22850 2890 23070 3110) + R(diff_cont 22850 -310 23070 -90) + R(diff_cont 22850 90 23070 310) + R(metal1 -1700 1620 -1340 1980) + R(via1 -1645 1675 -1395 1925) + R(via1 22835 1675 23085 1925) + R(metal2 -1720 1600 23160 2000) + R(metal2_lbl -1 1799 1 1801) + ) + N(OSC + R(diff_cont 24450 2890 24670 3110) + R(diff_cont 24450 2490 24670 2710) + R(diff_cont 24450 90 24670 310) + R(diff_cont 24450 -310 24670 -90) + R(via1 24435 1675 24685 1925) + R(metal2 24360 1600 24760 2000) + R(metal2_lbl 24559 1799 24561 1801) + ) + N(VDD + R(diff_cont 7810 2490 8030 2710) + R(diff_cont 7810 2890 8030 3110) + R(diff_cont 7810 2890 8030 3110) + R(diff_cont 7810 2490 8030 2710) + R(diff_cont 5170 2490 5390 2710) + R(diff_cont 5170 2890 5390 3110) + R(diff_cont 5170 2890 5390 3110) + R(diff_cont 5170 2490 5390 2710) + R(diff_cont 2530 2490 2750 2710) + R(diff_cont 2530 2890 2750 3110) + R(diff_cont 2530 2890 2750 3110) + R(diff_cont 2530 2490 2750 2710) + R(diff_cont -110 2490 110 2710) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2490 110 2710) + R(diff_cont 13090 2490 13310 2710) + R(diff_cont 13090 2890 13310 3110) + R(diff_cont 13090 2890 13310 3110) + R(diff_cont 13090 2490 13310 2710) + R(diff_cont 10450 2490 10670 2710) + R(diff_cont 10450 2890 10670 3110) + R(diff_cont 10450 2890 10670 3110) + R(diff_cont 10450 2490 10670 2710) + R(diff_cont 18370 2490 18590 2710) + R(diff_cont 18370 2890 18590 3110) + R(diff_cont 18370 2890 18590 3110) + R(diff_cont 18370 2490 18590 2710) + R(diff_cont 15730 2490 15950 2710) + R(diff_cont 15730 2890 15950 3110) + R(diff_cont 15730 2890 15950 3110) + R(diff_cont 15730 2490 15950 2710) + R(diff_cont 23650 2490 23870 2710) + R(diff_cont 23650 2890 23870 3110) + R(diff_cont 23650 2890 23870 3110) + R(diff_cont 23650 2490 23870 2710) + R(diff_cont 21010 2490 21230 2710) + R(diff_cont 21010 2890 21230 3110) + R(diff_cont 21010 2890 21230 3110) + R(diff_cont 21010 2490 21230 2710) + R(metal1 -180 3100 180 4220) + R(metal1 2460 3100 2820 4220) + R(metal1 5100 3100 5460 4220) + R(metal1 7740 3100 8100 4220) + R(metal1 10380 3100 10740 4220) + R(metal1 13020 3100 13380 4220) + R(metal1 15660 3100 16020 4220) + R(metal1 18300 3100 18660 4220) + R(metal1 20940 3100 21300 4220) + R(metal1 23580 3100 23940 4220) + R(metal1 7740 2420 8100 3180) + R(metal1 7740 2420 8100 3180) + R(metal1 5100 2420 5460 3180) + R(metal1 5100 2420 5460 3180) + R(metal1 2460 2420 2820 3180) + R(metal1 2460 2420 2820 3180) + R(metal1 -180 2420 180 3180) + R(metal1 -180 2420 180 3180) + R(metal1 13020 2420 13380 3180) + R(metal1 13020 2420 13380 3180) + R(metal1 10380 2420 10740 3180) + R(metal1 10380 2420 10740 3180) + R(metal1 18300 2420 18660 3180) + R(metal1 18300 2420 18660 3180) + R(metal1 15660 2420 16020 3180) + R(metal1 15660 2420 16020 3180) + R(metal1 23580 2420 23940 3180) + R(metal1 23580 2420 23940 3180) + R(metal1 20940 2420 21300 3180) + R(metal1 20940 2420 21300 3180) + R(metal2_lbl -1 2799 1 2801) + ) + N('BULK,VSS' + R(diff_cont 7810 -310 8030 -90) + R(diff_cont 7810 90 8030 310) + R(diff_cont 7810 90 8030 310) + R(diff_cont 7810 -310 8030 -90) + R(diff_cont 5170 -310 5390 -90) + R(diff_cont 5170 90 5390 310) + R(diff_cont 5170 90 5390 310) + R(diff_cont 5170 -310 5390 -90) + R(diff_cont 2530 -310 2750 -90) + R(diff_cont 2530 90 2750 310) + R(diff_cont 2530 90 2750 310) + R(diff_cont 2530 -310 2750 -90) + R(diff_cont -110 -310 110 -90) + R(diff_cont -110 90 110 310) + R(diff_cont -110 90 110 310) + R(diff_cont -110 -310 110 -90) + R(diff_cont 13090 -310 13310 -90) + R(diff_cont 13090 90 13310 310) + R(diff_cont 13090 90 13310 310) + R(diff_cont 13090 -310 13310 -90) + R(diff_cont 10450 -310 10670 -90) + R(diff_cont 10450 90 10670 310) + R(diff_cont 10450 90 10670 310) + R(diff_cont 10450 -310 10670 -90) + R(diff_cont 18370 -310 18590 -90) + R(diff_cont 18370 90 18590 310) + R(diff_cont 18370 90 18590 310) + R(diff_cont 18370 -310 18590 -90) + R(diff_cont 15730 -310 15950 -90) + R(diff_cont 15730 90 15950 310) + R(diff_cont 15730 90 15950 310) + R(diff_cont 15730 -310 15950 -90) + R(diff_cont 23650 -310 23870 -90) + R(diff_cont 23650 90 23870 310) + R(diff_cont 23650 90 23870 310) + R(diff_cont 23650 -310 23870 -90) + R(diff_cont 21010 -310 21230 -90) + R(diff_cont 21010 90 21230 310) + R(diff_cont 21010 90 21230 310) + R(diff_cont 21010 -310 21230 -90) + R(metal1 -180 -1420 180 -300) + R(metal1 2460 -1420 2820 -300) + R(metal1 5100 -1420 5460 -300) + R(metal1 7740 -1420 8100 -300) + R(metal1 10380 -1420 10740 -300) + R(metal1 13020 -1420 13380 -300) + R(metal1 15660 -1420 16020 -300) + R(metal1 18300 -1420 18660 -300) + R(metal1 20940 -1420 21300 -300) + R(metal1 23580 -1420 23940 -300) + R(metal1 7740 -380 8100 380) + R(metal1 7740 -380 8100 380) + R(metal1 5100 -380 5460 380) + R(metal1 5100 -380 5460 380) + R(metal1 2460 -380 2820 380) + R(metal1 2460 -380 2820 380) + R(metal1 -180 -380 180 380) + R(metal1 -180 -380 180 380) + R(metal1 13020 -380 13380 380) + R(metal1 13020 -380 13380 380) + R(metal1 10380 -380 10740 380) + R(metal1 10380 -380 10740 380) + R(metal1 18300 -380 18660 380) + R(metal1 18300 -380 18660 380) + R(metal1 15660 -380 16020 380) + R(metal1 15660 -380 16020 380) + R(metal1 23580 -380 23940 380) + R(metal1 23580 -380 23940 380) + R(metal1 20940 -380 21300 380) + R(metal1 20940 -380 21300 380) + R(metal2_lbl -1 -1 1 1) + ) + N($I13 + R(diff_cont 3330 2890 3550 3110) + R(diff_cont 3330 2490 3550 2710) + R(diff_cont 3330 90 3550 310) + R(diff_cont 3330 -310 3550 -90) + ) + N($I7 + R(diff_cont 19170 2890 19390 3110) + R(diff_cont 19170 2490 19390 2710) + R(diff_cont 19170 90 19390 310) + R(diff_cont 19170 -310 19390 -90) + ) + N($I6 + R(diff_cont 13890 2890 14110 3110) + R(diff_cont 13890 2490 14110 2710) + R(diff_cont 13890 90 14110 310) + R(diff_cont 13890 -310 14110 -90) + ) + N($I5 + R(diff_cont 8610 2890 8830 3110) + R(diff_cont 8610 2490 8830 2710) + R(diff_cont 8610 90 8830 310) + R(diff_cont 8610 -310 8830 -90) + ) + P(FB FB) + P(OSC OSC) + P(VDD VDD) + P('BULK,VSS' 'BULK,VSS') + X($1 INV2PAIR Y(19420 -800) + P(BULK 'BULK,VSS') + P($1 FB) + P($2 VDD) + P($3 'BULK,VSS') + P($4 $I7) + P($5 OSC) + P($6 VDD) + ) + X($2 INV2PAIR Y(-1700 -800) + P(BULK 'BULK,VSS') + P($2 VDD) + P($3 'BULK,VSS') + P($4 FB) + P($5 $I13) + P($6 VDD) + ) + X($3 INV2PAIR Y(3580 -800) + P(BULK 'BULK,VSS') + P($2 VDD) + P($3 'BULK,VSS') + P($4 $I13) + P($5 $I5) + P($6 VDD) + ) + X($4 INV2PAIR Y(8860 -800) + P(BULK 'BULK,VSS') + P($2 VDD) + P($3 'BULK,VSS') + P($4 $I5) + P($5 $I6) + P($6 VDD) + ) + X($5 INV2PAIR Y(14140 -800) + P(BULK 'BULK,VSS') + P($2 VDD) + P($3 'BULK,VSS') + P($4 $I6) + P($5 $I7) + P($6 VDD) + ) +) diff --git a/testdata/algo/l2n_writer_au_s.txt b/testdata/algo/l2n_writer_au_s.txt new file mode 100644 index 000000000..2ff91db45 --- /dev/null +++ b/testdata/algo/l2n_writer_au_s.txt @@ -0,0 +1,444 @@ +#%l2n-klayout +W(RINGO) +U(0.001) +L(poly) +L(poly_lbl) +L(diff_cont) +L(poly_cont) +L(metal1) +L(metal1_lbl) +L(via1) +L(metal2) +L(metal2_lbl) +L(psd) +L(nsd) +C(poly poly poly_lbl poly_cont) +C(poly_lbl poly) +C(diff_cont diff_cont metal1 psd nsd) +C(poly_cont poly poly_cont metal1) +C(metal1 diff_cont poly_cont metal1 metal1_lbl via1) +C(metal1_lbl metal1) +C(via1 metal1 via1 metal2) +C(metal2 via1 metal2 metal2_lbl) +C(metal2_lbl metal2) +C(psd diff_cont psd) +C(nsd diff_cont nsd) +D(D$PMOS PMOS + T(S + R(psd -650 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(psd 125 -475 675 475) + ) +) +D(D$PMOS$1 PMOS + T(S + R(psd -675 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(psd 125 -475 650 475) + ) +) +D(D$NMOS NMOS + T(S + R(nsd -650 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(nsd 125 -475 675 475) + ) +) +D(D$NMOS$1 NMOS + T(S + R(nsd -675 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(nsd 125 -475 650 475) + ) +) +X(INV2 + N(IN + R(poly -525 -250 -275 2250) + R(poly -1700 1620 -400 1980) + R(poly -525 -800 -275 800) + R(poly -525 2000 -275 3600) + R(poly_lbl -801 1799 -799 1801) + R(poly_cont -1630 1690 -1410 1910) + ) + N($2 + R(poly 275 -250 525 2250) + R(poly 220 820 580 1180) + R(poly 275 2000 525 3600) + R(poly 275 -800 525 800) + R(diff_cont -910 2490 -690 2710) + R(diff_cont -910 2890 -690 3110) + R(diff_cont -910 -310 -690 -90) + R(diff_cont -910 90 -690 310) + R(poly_cont 290 890 510 1110) + R(metal1 -800 820 580 1180) + R(metal1 -980 -420 -620 2420) + R(metal1 -980 2420 -620 3180) + R(metal1 -980 -380 -620 380) + R(psd -1050 2325 -525 3275) + R(nsd -1050 -475 -525 475) + ) + N(OUT + R(diff_cont 690 2890 910 3110) + R(diff_cont 690 2490 910 2710) + R(diff_cont 690 90 910 310) + R(diff_cont 690 -310 910 -90) + Q(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20) + R(metal1 620 2420 980 3180) + R(metal1 620 -380 980 380) + R(metal1_lbl 799 1799 801 1801) + R(psd 525 2325 1050 3275) + R(nsd 525 -475 1050 475) + ) + N($4 + R(diff_cont -110 -310 110 -90) + R(diff_cont -110 90 110 310) + R(diff_cont -110 90 110 310) + R(diff_cont -110 -310 110 -90) + R(metal1 -180 -380 180 380) + R(metal1 -180 -380 180 380) + R(via1 -125 -325 125 -75) + R(via1 -125 75 125 325) + R(metal2 -1400 -450 1400 450) + R(nsd -275 -475 275 475) + ) + N($5 + R(diff_cont -110 2490 110 2710) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2490 110 2710) + R(metal1 -180 2420 180 3180) + R(metal1 -180 2420 180 3180) + R(via1 -125 2475 125 2725) + R(via1 -125 2875 125 3125) + R(metal2 -1400 2350 1400 3250) + R(psd -275 2325 275 3275) + ) + P(IN IN) + P($1 $2) + P(OUT OUT) + P($3 $4) + P($4 $5) + D($1 D$PMOS + Y(-400 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.49875) + E(AD 0.26125) + T(S $2) + T(G IN) + T(D $5) + ) + D($2 D$PMOS$1 + Y(400 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.26125) + E(AD 0.49875) + T(S $5) + T(G $2) + T(D OUT) + ) + D($3 D$NMOS + Y(-400 0) + E(L 0.25) + E(W 0.95) + E(AS 0.49875) + E(AD 0.26125) + T(S $2) + T(G IN) + T(D $4) + ) + D($4 D$NMOS$1 + Y(400 0) + E(L 0.25) + E(W 0.95) + E(AS 0.26125) + E(AD 0.49875) + T(S $4) + T(G $2) + T(D OUT) + ) +) +X(RINGO + N(FB + R(diff_cont 22850 2490 23070 2710) + R(diff_cont 22850 2890 23070 3110) + R(diff_cont 22850 -310 23070 -90) + R(diff_cont 22850 90 23070 310) + R(metal1 -1700 1620 -1340 1980) + R(via1 -1645 1675 -1395 1925) + R(via1 22835 1675 23085 1925) + R(metal2 -1720 1600 23160 2000) + R(metal2_lbl -1 1799 1 1801) + ) + N(OSC + R(diff_cont 24450 2890 24670 3110) + R(diff_cont 24450 2490 24670 2710) + R(diff_cont 24450 90 24670 310) + R(diff_cont 24450 -310 24670 -90) + R(via1 24435 1675 24685 1925) + R(metal2 24360 1600 24760 2000) + R(metal2_lbl 24559 1799 24561 1801) + ) + N(VSS + R(diff_cont 2530 -310 2750 -90) + R(diff_cont 2530 90 2750 310) + R(diff_cont 2530 90 2750 310) + R(diff_cont 2530 -310 2750 -90) + R(diff_cont -110 -310 110 -90) + R(diff_cont -110 90 110 310) + R(diff_cont -110 90 110 310) + R(diff_cont -110 -310 110 -90) + R(diff_cont 5170 -310 5390 -90) + R(diff_cont 5170 90 5390 310) + R(diff_cont 5170 90 5390 310) + R(diff_cont 5170 -310 5390 -90) + R(diff_cont 7810 -310 8030 -90) + R(diff_cont 7810 90 8030 310) + R(diff_cont 7810 90 8030 310) + R(diff_cont 7810 -310 8030 -90) + R(diff_cont 10450 -310 10670 -90) + R(diff_cont 10450 90 10670 310) + R(diff_cont 10450 90 10670 310) + R(diff_cont 10450 -310 10670 -90) + R(diff_cont 13090 -310 13310 -90) + R(diff_cont 13090 90 13310 310) + R(diff_cont 13090 90 13310 310) + R(diff_cont 13090 -310 13310 -90) + R(diff_cont 15730 -310 15950 -90) + R(diff_cont 15730 90 15950 310) + R(diff_cont 15730 90 15950 310) + R(diff_cont 15730 -310 15950 -90) + R(diff_cont 18370 -310 18590 -90) + R(diff_cont 18370 90 18590 310) + R(diff_cont 18370 90 18590 310) + R(diff_cont 18370 -310 18590 -90) + R(diff_cont 21010 -310 21230 -90) + R(diff_cont 21010 90 21230 310) + R(diff_cont 21010 90 21230 310) + R(diff_cont 21010 -310 21230 -90) + R(diff_cont 23650 -310 23870 -90) + R(diff_cont 23650 90 23870 310) + R(diff_cont 23650 90 23870 310) + R(diff_cont 23650 -310 23870 -90) + R(metal1 2460 -380 2820 380) + R(metal1 2460 -380 2820 380) + R(metal1 -180 -380 180 380) + R(metal1 -180 -380 180 380) + R(metal1 5100 -380 5460 380) + R(metal1 5100 -380 5460 380) + R(metal1 7740 -380 8100 380) + R(metal1 7740 -380 8100 380) + R(metal1 10380 -380 10740 380) + R(metal1 10380 -380 10740 380) + R(metal1 13020 -380 13380 380) + R(metal1 13020 -380 13380 380) + R(metal1 15660 -380 16020 380) + R(metal1 15660 -380 16020 380) + R(metal1 18300 -380 18660 380) + R(metal1 18300 -380 18660 380) + R(metal1 20940 -380 21300 380) + R(metal1 20940 -380 21300 380) + R(metal1 23580 -380 23940 380) + R(metal1 23580 -380 23940 380) + R(metal2_lbl -1 -1 1 1) + ) + N(VDD + R(diff_cont 2530 2490 2750 2710) + R(diff_cont 2530 2890 2750 3110) + R(diff_cont 2530 2890 2750 3110) + R(diff_cont 2530 2490 2750 2710) + R(diff_cont -110 2490 110 2710) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2490 110 2710) + R(diff_cont 5170 2490 5390 2710) + R(diff_cont 5170 2890 5390 3110) + R(diff_cont 5170 2890 5390 3110) + R(diff_cont 5170 2490 5390 2710) + R(diff_cont 7810 2490 8030 2710) + R(diff_cont 7810 2890 8030 3110) + R(diff_cont 7810 2890 8030 3110) + R(diff_cont 7810 2490 8030 2710) + R(diff_cont 10450 2490 10670 2710) + R(diff_cont 10450 2890 10670 3110) + R(diff_cont 10450 2890 10670 3110) + R(diff_cont 10450 2490 10670 2710) + R(diff_cont 13090 2490 13310 2710) + R(diff_cont 13090 2890 13310 3110) + R(diff_cont 13090 2890 13310 3110) + R(diff_cont 13090 2490 13310 2710) + R(diff_cont 15730 2490 15950 2710) + R(diff_cont 15730 2890 15950 3110) + R(diff_cont 15730 2890 15950 3110) + R(diff_cont 15730 2490 15950 2710) + R(diff_cont 18370 2490 18590 2710) + R(diff_cont 18370 2890 18590 3110) + R(diff_cont 18370 2890 18590 3110) + R(diff_cont 18370 2490 18590 2710) + R(diff_cont 21010 2490 21230 2710) + R(diff_cont 21010 2890 21230 3110) + R(diff_cont 21010 2890 21230 3110) + R(diff_cont 21010 2490 21230 2710) + R(diff_cont 23650 2490 23870 2710) + R(diff_cont 23650 2890 23870 3110) + R(diff_cont 23650 2890 23870 3110) + R(diff_cont 23650 2490 23870 2710) + R(metal1 2460 2420 2820 3180) + R(metal1 2460 2420 2820 3180) + R(metal1 -180 2420 180 3180) + R(metal1 -180 2420 180 3180) + R(metal1 5100 2420 5460 3180) + R(metal1 5100 2420 5460 3180) + R(metal1 7740 2420 8100 3180) + R(metal1 7740 2420 8100 3180) + R(metal1 10380 2420 10740 3180) + R(metal1 10380 2420 10740 3180) + R(metal1 13020 2420 13380 3180) + R(metal1 13020 2420 13380 3180) + R(metal1 15660 2420 16020 3180) + R(metal1 15660 2420 16020 3180) + R(metal1 18300 2420 18660 3180) + R(metal1 18300 2420 18660 3180) + R(metal1 20940 2420 21300 3180) + R(metal1 20940 2420 21300 3180) + R(metal1 23580 2420 23940 3180) + R(metal1 23580 2420 23940 3180) + R(metal2_lbl -1 2799 1 2801) + ) + N($I19 + R(diff_cont 690 2890 910 3110) + R(diff_cont 690 2490 910 2710) + R(diff_cont 690 90 910 310) + R(diff_cont 690 -310 910 -90) + ) + N($I8 + R(diff_cont 21810 2890 22030 3110) + R(diff_cont 21810 2490 22030 2710) + R(diff_cont 21810 90 22030 310) + R(diff_cont 21810 -310 22030 -90) + ) + N($I7 + R(diff_cont 19170 2890 19390 3110) + R(diff_cont 19170 2490 19390 2710) + R(diff_cont 19170 90 19390 310) + R(diff_cont 19170 -310 19390 -90) + ) + N($I6 + R(diff_cont 16530 2890 16750 3110) + R(diff_cont 16530 2490 16750 2710) + R(diff_cont 16530 90 16750 310) + R(diff_cont 16530 -310 16750 -90) + ) + N($I5 + R(diff_cont 13890 2890 14110 3110) + R(diff_cont 13890 2490 14110 2710) + R(diff_cont 13890 90 14110 310) + R(diff_cont 13890 -310 14110 -90) + ) + N($I4 + R(diff_cont 11250 2890 11470 3110) + R(diff_cont 11250 2490 11470 2710) + R(diff_cont 11250 90 11470 310) + R(diff_cont 11250 -310 11470 -90) + ) + N($I3 + R(diff_cont 8610 2890 8830 3110) + R(diff_cont 8610 2490 8830 2710) + R(diff_cont 8610 90 8830 310) + R(diff_cont 8610 -310 8830 -90) + ) + N($I2 + R(diff_cont 5970 2890 6190 3110) + R(diff_cont 5970 2490 6190 2710) + R(diff_cont 5970 90 6190 310) + R(diff_cont 5970 -310 6190 -90) + ) + N($I1 + R(diff_cont 3330 2890 3550 3110) + R(diff_cont 3330 2490 3550 2710) + R(diff_cont 3330 90 3550 310) + R(diff_cont 3330 -310 3550 -90) + ) + P(FB FB) + P(OSC OSC) + P(VSS VSS) + P(VDD VDD) + X($1 INV2 Y(23760 0) + P(IN $I8) + P($1 FB) + P(OUT OSC) + P($3 VSS) + P($4 VDD) + ) + X($2 INV2 Y(0 0) + P(IN FB) + P(OUT $I19) + P($3 VSS) + P($4 VDD) + ) + X($3 INV2 Y(2640 0) + P(IN $I19) + P(OUT $I1) + P($3 VSS) + P($4 VDD) + ) + X($4 INV2 Y(5280 0) + P(IN $I1) + P(OUT $I2) + P($3 VSS) + P($4 VDD) + ) + X($5 INV2 Y(7920 0) + P(IN $I2) + P(OUT $I3) + P($3 VSS) + P($4 VDD) + ) + X($6 INV2 Y(10560 0) + P(IN $I3) + P(OUT $I4) + P($3 VSS) + P($4 VDD) + ) + X($7 INV2 Y(13200 0) + P(IN $I4) + P(OUT $I5) + P($3 VSS) + P($4 VDD) + ) + X($8 INV2 Y(15840 0) + P(IN $I5) + P(OUT $I6) + P($3 VSS) + P($4 VDD) + ) + X($9 INV2 Y(18480 0) + P(IN $I6) + P(OUT $I7) + P($3 VSS) + P($4 VDD) + ) + X($10 INV2 Y(21120 0) + P(IN $I7) + P(OUT $I8) + P($3 VSS) + P($4 VDD) + ) +) From 4c7f43d749ff9311c4e8cdc752d0c2d29191d176 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 20 Jan 2019 17:31:58 +0100 Subject: [PATCH 203/335] More l2n reader tests. --- .../dbLayoutToNetlistReaderTests.cc | 72 +++++++++++++++++- .../dbLayoutToNetlistWriterTests.cc | 2 +- testdata/algo/l2n_reader_au.gds | Bin 0 -> 18382 bytes testdata/algo/l2n_reader_au_2.gds | Bin 0 -> 27844 bytes 4 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 testdata/algo/l2n_reader_au.gds create mode 100644 testdata/algo/l2n_reader_au_2.gds diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc index 549466153..83aaeab10 100644 --- a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -89,13 +89,13 @@ TEST(1_ReaderBasic) std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); au = tl::combine_path (au, "algo"); - au = tl::combine_path (au, "l2n_writer_au.gds"); + au = tl::combine_path (au, "l2n_reader_au.gds"); db::compare_layouts (_this, ly2, au); } } -TEST(2_ReaderBasicShort) +TEST(1b_ReaderBasicShort) { db::Layout ly; @@ -129,3 +129,71 @@ TEST(2_ReaderBasicShort) } } + +TEST(2_ReaderWithGlobalNets) +{ + db::Layout ly; + + db::Cell &tc = ly.cell (ly.add_cell ("TOP")); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2.txt"); + tl::InputStream is_in (in_path); + + db::LayoutToNetlistStandardReader reader (is_in); + reader.read (&l2n); + + // verify against the input + + std::string path = tmp_file ("tmp_l2nreader_2.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + + // test build_all_nets from read l2n + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = reader.layer_by_name ("psd"); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = reader.layer_by_name ("nsd"); + lmap [ly2.insert_layer (db::LayerProperties (12, 0))] = reader.layer_by_name ("rbulk"); + lmap [ly2.insert_layer (db::LayerProperties (13, 0))] = reader.layer_by_name ("ptie"); + lmap [ly2.insert_layer (db::LayerProperties (14, 0))] = reader.layer_by_name ("ntie"); + lmap [ly2.insert_layer (db::LayerProperties (1, 0)) ] = reader.layer_by_name ("nwell"); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = reader.layer_by_name ("poly"); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = reader.layer_by_name ("diff_cont"); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = reader.layer_by_name ("poly_cont"); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = reader.layer_by_name ("metal1"); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = reader.layer_by_name ("via1"); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = reader.layer_by_name ("metal2"); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "l2n_reader_au_2.gds"); + + db::compare_layouts (_this, ly2, au); + } +} + diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 54f6f4b60..02d81d3e3 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -429,7 +429,7 @@ TEST(2_WriterWithGlobalNets) } } - // test build_all_nets + // test build_all_nets as reference for the reader { db::Layout ly2; diff --git a/testdata/algo/l2n_reader_au.gds b/testdata/algo/l2n_reader_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..a839f25846ba697c56f553df6736781bc786bdd8 GIT binary patch literal 18382 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/l2n_reader_au_2.gds b/testdata/algo/l2n_reader_au_2.gds new file mode 100644 index 0000000000000000000000000000000000000000..f34c627da51201d63bf6d2f5e2b2257897bc3bfd GIT binary patch literal 27844 zcmd^{UyL8sb;rlM>;325_1f4x1lKsu1{ujs?cK#r6R?a~2OCpEY{yD08zMjSp`ukp z5sE5`DnhE1(kh}Tl~RNvm(og!q7Fl?RfCD9TfdP()ExrAdSuUNC02=QHPi zeecZgH+N?J+vO#^e9gJ@z31F>?m2Vk%x|_BEry0ZTkO~{H1=P`hGJW>rTDGlBk8}3 z@uAUsj~^+DZNnQ6-T4;>PyFB?-~QdD@BPj<{_fnFV#mg2-i@Q9OGnQhzw79OOLHfG z@4nM>`{x#lVr0B1MyH30O~vTUP*D_nXNOl--oLQ2viwo7C^r1(%F4=@gpz~B$QI*| z6RK23>NL%NTFz;RuA&(Fd!gi=b@o~5eAGIXu5{%~%&HY)g==+dkayPFv(m)7 z>!mYY>HR%1-S7ddwA7l!biH);S!oi}$#kXbw0p~yCNWKPuJ`WUA9wF?y3+OBy@_|L zwc%0EnD-x5O%O`nIb-_7^aEaL8PjB?NldN!t=&qu)~ew8$1~N)D%W3QPyI_`ne3oG zx$;z~^iP#LM|XnN-d)OACOVT?uBlG*>|MuC-SwH{XC6F$^6dQFfnsFR?#5edcUP;4 zqaWH}nx}5sc;~Dk|4r@Kc)0ur|K?7+{Dty=%eC4pH`Zq<@6oOEnE$>S=jnGxU;d?M z=khPz*BfpSN?!WDv?K6GDwXCL8-LC2hlVRJnyx>JDEabhej@14H_kcuruP8+h>{oQ z)|0<#`$sG5zt{Lrn*H~h-H)05-xW#@R_q^?KUUGtIpq7M-*Dw$OuwC$gpx0RzoNg! zH_i$8X7^F}5hX89RQThm{wrTG{fAaou6)IKmy2TK7lo38&Tlyp*7N7K{`q5f+SYw< zI$QI{s(b4^wwB)fnNHWg{Dqa3cWwWJS0Cp3h?2jX-T&sh@0kBBf8KlA{BQXql>Ewf zuFHRmJ^m{zVfXqo`L^q=)gP~?JR@}UpXLeqKU4KzQOrEz@!GP({VkMy=8?+&tDGd+ zcVpwe|F*p4`%ftO*m#9st6xR2In9M*$vE6a%hvt8)|3b+pw&(pf@iP0jC?>zg{*5U4#LId9rhA+}b&KCq-;@7f`mKHoC7<|a#eY5g zO`mlB)NRfeO1|ln6@IP$+xkD|`8V~F=bupWiH|vdmH9K@KYzOH`!4xoC&E?MuYRYw z?$(%ZGpD)k)@Yu7+5Fyrdp3hpKeQ*6Uq>XHeY_IQlaFH znBm*q`)AG{-sOBp$xBT6mAeTi0ICT%`8D(3$%vBYsv$h89yoQ2^M#VM&)ao-y(+)|tWa_=qd)NAPd#MoH@=W+?(^pOHorX$aNUN-obM<( z==@by!~JD7+?qTG`qO)B4Ks~)7S(uQNNxIUreqi34==)d|i*FU_=^*c%q=J-?&=g-VKUnqI4awI$7 z?7n37f8BA+`a@S9xA_7}KKgan-xB%RY9FEhU9kK8B8hxYFQWS_wb&F;hJQ*`bR2ud&TQMrIY-T7nEG4#>@857hj}^cGM|unkvE@t z8MpO5>DQHZ8sAa!5=;FpxiV$_)9})&^^Mb{u5G=toHeu!+umc9vzDc}?Mmxvr~SeE z?>v4g^a!mYrz0k9t?Cxh7tHg!JW5{GS3k$>J8_P1o~-Q5**~|){Jbc@p&t~*mWvT3 zH%xa*51;!!e9i&zg_76K18dHgo!4XksAV&J5dPTKh?3{l507<%tQ)>i^4$8@f-gHe z+F!}^*~7Me+h-u>u`T};QF6mH{%gwL__pf@(LeUti0BXFjrv!^Z>co>ve!G5_3XWE z4YIe>9BKAmhxPUx)u(POu7UNP<`~d=@2=H-zpw_@cbX%$zNvCf{fAe+>3{XgCzRYU z-MKxLZ{`o2zxh+n7fNopu6)@kyM>bH?5A>Z9SD!gCzQO6&pP3=ZummUbL)qv>%Hi6>9=lCX~&j*u3sp*x=F!* zJ@yy&AL|%B?0li*hN=Ea?Hs<*GZ`e4hyO1knHwPkUj!)ZbDGt%Flm!sAwe zc*?nslSX>Kvl;478lCQ$ZTGGT+a>Hwkexa?A5rq$j)g~NfMg7Oq2z{X#3~sFkDUp! zQ{f9GukF|#KA8cZOo1Q`C^2NqAwyKWUe%pv_jknmS`|E9=m^=RQ>p#&P+ebBQj>?kdJ$+}8KJ-z0 zmmrip*N=ur-y58|#rZ-ZJR<$*Fy6SU&};qfQa^og>=#OI*d>qU z@x{SBzF<2f=ka|*?r{)L?|UO69uMP~<2w#T#N%PSam9?|SrAXd7fPP<_)Qnx;~*a2 zVZUJ%;_)z^)sJVS%ySM5Qm!*kN~)%aGq zVV+Lsgx$}@dBVxCa-aS1CHFmu&v!o_5#NXLM&B_`j6q@#Unsd@mXD9&<8$~z$>sb0 z_IvC{F@rCZJQq87#0Vs2@P(4+Vt3$(#|R{E5AKXe?85k}Vt24PcAfI&#Ou-L4YBK# zziqL*=S`0tNbF9$8j;wA@y6IOPK-ce245(7E_U#U5lGD73nkCR?w&V2b|A4k@oGe3 z7sgi=yZ!#=_B2S{YFG$7sk89&VEtE)9{hc+t?67jvaEZ)9)8L z%+tZ1PW!2YJ)P$1V2{P+YU}>)U{9xcI@#0VKG(^f4)b)fC#|`YMc=bmCwtO)`sM%5 zCp>>Z^5^CUB9cF0yfOcGKH>QblD9WM5Rv>1Hfj&Ieil_L+#RKa4lpzs-6% ztP^DY+h-z5o~vJj^9Qqk;!Wp+tba1x`#{zo#v9i^@usgIWc`z`Mr8eAd>#4G&ehv* zYkmg4BmCchFEcC8Obff+e<15$SdK{i!+2x-7Iu65LE^u#9Fh2k@pa@6X8)d_I3HyF zw|+Y!>ks3N_V4+LuODRnw|+Y!>ks4W$REu9yDvK*Wc|mUkI4GNc%%JyU-tEbtpC{a z5m|p2Uq}96_TTZa^Fh{ssQebhk@bi1M*Hu0*w+uT{zE4tvi>l>j{L#wKY!8rAnU(y zC?e|*GKzlzBE z!}vP#S8adtHq_Q?U*=yazs$6Gerjv^ZbZqiJW}a-C49TTKL_6t{-zP68~J72>`na6+bwVGt@_RO2HVl*Ddi=u8^7FlrssxVxZdq&gswk~H|ighPyVpZ z344>zx;KpoCC{&aFuv#i2mF^gD*0RHjjezFbzk@5lF-*5#vA=VD8I~m)4#`liGlxb zeMji}!+4|qLHT9gn*O^@H~NpgC3O8^yixz4{Ic$t{yX-%-b2p{U4IyF)ITV{tQ)5P z{0`T9!TyaO$~scYOFJ6%56Um=p6U7Yk6iERMWL@hj5q3E4Zqtvo3l$x&3*q)zq;?x zGwk&3L5F!d*wd+J*ukDo^K`JMQ_rx2J)P$1WKV~lVJCY!%+tx9v}ZV3^zA)%vL~IV zfA7)mdoO#Bkaq_34uz8E-W||eTyp1nAWw%iBoc;6$xekO!?@%at8y`F2V>f)E_6ubC-}q-zEJX9 z{NU+2@r07M@nxs%7D}G8pWYzXf$-=Z3MFsjV<&v%TnECVcPNy+jgOu1 zu^YZn@|^wfbe(uY$=mp{Q+5j_&)H9Jkn2Er^bUoRxACzPK6b+wN}jVHo~{#5D0v%S zcFJy{-C}=_h~C4@%ywe{eo| zkDjs)m|pT9{h;JI{e$w!TlAE9YL2<+$#eQw!|&F6yzlf~&ArD?zZFh_(I7IGy1JB{2F|#KQF?M zD7j(EPyUju@&4Gp&G<{lu`e6{5A7R`fRY=g{Nyjm8gGyB4?FU=IzRtC;|G-7u;jPI z-2NsPzjyuAk@Ay9qpcBJv1?B6Tw@H^#dZ@1sR`P>nYJ4n3G?Tkp=!+7eq zO57P|e}U{f_(I8ZXWh>o@wkJ;{oKxo#665>^|Qaw&wfL{Q1aToOU}CZ$Kr19#`TvU zul&2k{S|xnYAAW_tMz2x8Sih{caCFUHr`*c?;Its?Yo3;b}ztpWWS#KZQr+` zZ%ywpyY8?f`}N!xecytTcigx3Z42MPUi*6)<6Hfk*zzma+V{wnvDtU*JKkNM8@5;N z3)yR5KbKsq4E$Hd(8g!Nxc{Aqo-10i%f8e-bDw=|#@9Ed>6*pmJhiN;jhmOwSAHE` z8}E7Z&@*0p6VNlBcIM4K`xZD Date: Sun, 20 Jan 2019 23:12:27 +0100 Subject: [PATCH 204/335] Refactoring, some bugfixes, GSI bindings for L2N methods. --- src/db/db/dbDeepRegion.h | 4 +- src/db/db/dbDeepShapeStore.cc | 6 +- src/db/db/dbHierarchyBuilder.cc | 32 ++- src/db/db/dbLayoutToNetlist.cc | 136 ++++++++++-- src/db/db/dbLayoutToNetlist.h | 90 +++++++- src/db/db/dbLayoutToNetlistReader.cc | 68 ++---- src/db/db/dbLayoutToNetlistReader.h | 3 - src/db/db/dbLayoutToNetlistWriter.cc | 25 +-- src/db/db/dbRegion.h | 44 +++- src/db/db/gsiDeclDbLayoutToNetlist.cc | 82 +++++++- src/db/unit_tests/dbHierarchyBuilderTests.cc | 85 ++++++++ .../dbLayoutToNetlistReaderTests.cc | 59 +++--- src/db/unit_tests/dbLayoutToNetlistTests.cc | 194 ++++++++++++------ .../dbLayoutToNetlistWriterTests.cc | 28 +-- testdata/algo/hierarchy_builder_au2f.gds | Bin 0 -> 11978 bytes testdata/ruby/dbLayoutToNetlist.rb | 110 ++++++---- 16 files changed, 682 insertions(+), 284 deletions(-) create mode 100644 testdata/algo/hierarchy_builder_au2f.gds diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 00db14b56..2d9a1e509 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -46,6 +46,7 @@ public: DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 3.0, size_t max_vertex_count = 16); DeepRegion (const DeepRegion &other); + DeepRegion (const DeepLayer &dl); virtual ~DeepRegion (); @@ -94,10 +95,9 @@ protected: private: DeepRegion &operator= (const DeepRegion &other); - DeepRegion (const DeepLayer &dl); DeepLayer m_deep_layer; - // @@@ have hierarchical merged polygons later + // TODO: have hierarchical merged polygons later mutable db::Shapes m_merged_polygons; mutable bool m_merged_polygons_valid; diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 438456bc4..b7bf576d3 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -195,7 +195,7 @@ struct DeepShapeStore::LayoutHolder void remove_layer_ref (unsigned int layer) { if ((layer_refs[layer] -= 1) <= 0) { - layout.clear_layer (layer); + layout.delete_layer (layer); layer_refs.erase (layer); } } @@ -343,7 +343,9 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator db::Layout &layout = m_layouts.back ()->layout; layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier); - layout.dbu (si.layout ()->dbu ()); + if (si.layout ()) { + layout.dbu (si.layout ()->dbu ()); + } m_layout_map[si] = layout_index; diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 8c4e409ea..5331f1cad 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -36,13 +36,22 @@ static HierarchyBuilderShapeInserter def_inserter; int compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2) { + if ((iter1.layout () == 0) != (iter2.layout () == 0)) { + return (iter1.layout () == 0) < (iter2.layout () == 0); + } + if ((iter1.top_cell () == 0) != (iter2.top_cell () == 0)) { + return (iter1.top_cell () == 0) < (iter2.top_cell () == 0); + } + // basic source (layout, top_cell) needs to be the same of course if (iter1.layout () != iter2.layout ()) { // NOTE: pointer compare :-( return iter1.layout () < iter2.layout () ? -1 : 1; } - if (iter1.top_cell ()->cell_index () != iter2.top_cell ()->cell_index ()) { - return iter1.top_cell ()->cell_index () < iter2.top_cell ()->cell_index () ? -1 : 1; + if (iter1.top_cell ()) { + if (iter1.top_cell ()->cell_index () != iter2.top_cell ()->cell_index ()) { + return iter1.top_cell ()->cell_index () < iter2.top_cell ()->cell_index () ? -1 : 1; + } } // max depth controls the main hierarchical appearance @@ -51,10 +60,11 @@ compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIter } // if a region is set, the hierarchical appearance is the same only if the layers and - // complex region are indentical + // complex region are identical if ((iter1.region () == db::Box::world ()) != (iter2.region () == db::Box::world ())) { return (iter1.region () == db::Box::world ()) < (iter2.region () == db::Box::world ()) ? -1 : 1; } + if (iter1.region () != db::Box::world ()) { if (iter1.has_complex_region () != iter2.has_complex_region ()) { return iter1.has_complex_region () < iter2.has_complex_region () ? -1 : 1; @@ -174,30 +184,36 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter) m_cell_stack.clear (); m_cells_seen.clear (); + if (! iter->layout () || ! iter->top_cell ()) { + return; + } + std::pair > key (iter->top_cell ()->cell_index (), std::set ()); m_cm_entry = m_cell_map.find (key); - m_cm_new_entry = false; if (m_cm_entry == m_cell_map.end ()) { db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.first)); m_cm_entry = m_cell_map.insert (std::make_pair (key, new_top_index)).first; - m_cm_new_entry = true; } db::Cell &new_top = mp_target->cell (m_cm_entry->second); m_cells_seen.insert (key); + // NOTE: we consider the top cell "new" if it does not have instances. + // We can do so as the recursive shape iterator will always deliver all instances + // and not a partial set of instances. + m_cm_new_entry = new_top.begin ().at_end (); m_cell_stack.push_back (std::make_pair (m_cm_new_entry, &new_top)); } void -HierarchyBuilder::end (const RecursiveShapeIterator * /*iter*/) +HierarchyBuilder::end (const RecursiveShapeIterator *iter) { - tl_assert (m_cell_stack.size () == 1); + tl_assert (! iter->layout () || ! iter->top_cell () || m_cell_stack.size () == 1); m_initial_pass = false; m_cells_seen.clear (); - mp_initial_cell = m_cell_stack.front ().second; + mp_initial_cell = m_cell_stack.empty () ? 0 : m_cell_stack.front ().second; m_cell_stack.clear (); m_cm_entry = cell_map_type::const_iterator (); m_cm_new_entry = false; diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 73a9608e9..db9bed42b 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -36,6 +36,7 @@ static bool is_deep (const db::Region &r) } // the iterator provides the hierarchical selection (enabling/disabling cells etc.) + LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) : m_iter (iter), m_netlist_extracted (false) { @@ -44,6 +45,17 @@ LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) throw tl::Exception (tl::to_string (tr ("The netlist extractor cannot work on clipped layouts"))); } + init (); +} + +LayoutToNetlist::LayoutToNetlist () + : m_iter (), m_netlist_extracted (false) +{ + init (); +} + +void LayoutToNetlist::init () +{ m_dss.set_text_enlargement (1); m_dss.set_text_property_name (tl::Variant ("LABEL")); } @@ -83,11 +95,11 @@ db::Region *LayoutToNetlist::make_layer (const std::string &n) db::RecursiveShapeIterator si (m_iter); si.shape_flags (db::ShapeIterator::Nothing); - db::Region *region = new db::Region (si, m_dss); + std::auto_ptr region (new db::Region (si, m_dss)); if (! n.empty ()) { - name (*region, n); + register_layer (*region, n); } - return region; + return region.release (); } db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::string &n) @@ -96,11 +108,11 @@ db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::st si.set_layer (layer_index); si.shape_flags (db::ShapeIterator::All); - db::Region *region = new db::Region (si, m_dss); + std::auto_ptr region (new db::Region (si, m_dss)); if (! n.empty ()) { - name (*region, n); + register_layer (*region, n); } - return region; + return region.release (); } db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index, const std::string &n) @@ -109,11 +121,11 @@ db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index, const st si.set_layer (layer_index); si.shape_flags (db::ShapeIterator::Texts); - db::Region *region = new db::Region (si, m_dss); + std::auto_ptr region (new db::Region (si, m_dss)); if (! n.empty ()) { - name (*region, n); + register_layer (*region, n); } - return region; + return region.release (); } db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const std::string &n) @@ -122,11 +134,11 @@ db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const si.set_layer (layer_index); si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes); - db::Region *region = new db::Region (si, m_dss); + std::auto_ptr region (new db::Region (si, m_dss)); if (! n.empty ()) { - name (*region, n); + register_layer (*region, n); } - return region; + return region.release (); } void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers) @@ -148,6 +160,9 @@ void LayoutToNetlist::connect (const db::Region &l) if (! is_deep (l)) { throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in intra-layer connectivity for netlist extraction")))); } + if (! is_persisted (l)) { + throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in intra-layer connectivity for netlist extraction")))); + } // we need to keep a reference, so we can safely delete the region db::DeepLayer dl (l); @@ -167,6 +182,12 @@ void LayoutToNetlist::connect (const db::Region &a, const db::Region &b) if (! is_deep (b)) { throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in inter-layer connectivity (second layer) for netlist extraction")))); } + if (! is_persisted (a)) { + throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in inter-layer connectivity (first layer) for netlist extraction")))); + } + if (! is_persisted (b)) { + throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in inter-layer connectivity (second layer) for netlist extraction")))); + } // we need to keep a reference, so we can safely delete the region db::DeepLayer dla (a), dlb (b); @@ -182,7 +203,10 @@ size_t LayoutToNetlist::connect_global (const db::Region &l, const std::string & throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); } if (! is_deep (l)) { - throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in intra-layer connectivity for netlist extraction")))); + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in global connectivity for netlist extraction")))); + } + if (! is_persisted (l)) { + throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in global connectivity for netlist extraction")))); } // we need to keep a reference, so we can safely delete the region @@ -232,6 +256,14 @@ const db::Cell *LayoutToNetlist::internal_top_cell () const return &m_dss.const_initial_cell (); } +void LayoutToNetlist::ensure_internal_layout () +{ + if (m_dss.layouts () == 0) { + // the dummy layer acts as a reference holder for the layout + m_dummy_layer = m_dss.create_polygon_layer (db::RecursiveShapeIterator ()); + } +} + db::Layout *LayoutToNetlist::internal_layout () { return &m_dss.layout (); @@ -242,13 +274,69 @@ db::Cell *LayoutToNetlist::internal_top_cell () return &m_dss.initial_cell (); } -void LayoutToNetlist::name (const db::Region ®ion, const std::string &name) +void LayoutToNetlist::register_layer (const db::Region ®ion, const std::string &n) { - unsigned int li = layer_of (region); - db::Layout &ly = m_dss.layout (); - db::LayerProperties lp = ly.get_properties (li); - lp.name = name; - ly.set_properties (li, lp); + if (m_named_regions.find (n) != m_named_regions.end ()) { + throw tl::Exception (tl::to_string (tr ("Layer name is already used: ")) + n); + } + + db::DeepRegion *delegate = dynamic_cast (region.delegate()); + if (! delegate) { + throw tl::Exception (tl::to_string (tr ("Layer is not a deep region"))); + } + + if (is_persisted (region)) { + std::string prev_name = name (region); + m_named_regions.erase (prev_name); + } + + m_named_regions [n] = delegate->deep_layer (); + m_name_of_layer [layer_of (region)] = n; +} + +std::string LayoutToNetlist::name (const db::Region ®ion) const +{ + std::map::const_iterator n = m_name_of_layer.find (layer_of (region)); + if (n != m_name_of_layer.end ()) { + return n->second; + } else { + return std::string (); + } +} + +std::string LayoutToNetlist::name (unsigned int l) const +{ + std::map::const_iterator n = m_name_of_layer.find (l); + if (n != m_name_of_layer.end ()) { + return n->second; + } else { + return std::string (); + } +} + +bool LayoutToNetlist::is_persisted (const db::Region ®ion) const +{ + return m_name_of_layer.find (layer_of (region)) != m_name_of_layer.end (); +} + +db::Region *LayoutToNetlist::layer_by_name (const std::string &name) +{ + std::map::const_iterator l = m_named_regions.find (name); + if (l == m_named_regions.end ()) { + return 0; + } else { + return new db::Region (new db::DeepRegion (l->second)); + } +} + +db::Region *LayoutToNetlist::layer_by_index (unsigned int index) +{ + std::map::const_iterator n = m_name_of_layer.find (index); + if (n == m_name_of_layer.end ()) { + return 0; + } else { + return layer_by_name (n->second); + } } unsigned int LayoutToNetlist::layer_of (const db::Region ®ion) const @@ -433,8 +521,10 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & bool consider_cell = any_connections; for (std::map::const_iterator l = lmap.begin (); l != lmap.end () && !consider_cell; ++l) { - StopOnFirst sof; - consider_cell = deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, sof); + if (l->second) { + StopOnFirst sof; + consider_cell = !deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, sof); + } } if (! consider_cell) { @@ -452,7 +542,9 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & } for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { - deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, target_cell->shapes (l->first)); + if (l->second) { + deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, target_cell->shapes (l->first)); + } } if (! circuit_cell_name_prefix && ! device_cell_name_prefix) { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index c332d48a0..4030a5a00 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -73,6 +73,8 @@ class DB_PUBLIC LayoutToNetlist : public gsi::ObjectBase, public tl::Object { public: + typedef std::map::const_iterator layer_iterator; + /** * @brief The constructor * @@ -80,6 +82,11 @@ public: */ LayoutToNetlist (const db::RecursiveShapeIterator &iter); + /** + * @brief The default constructor + */ + LayoutToNetlist (); + /** * @brief Sets the number of threads to use for operations which support multiple threads */ @@ -115,28 +122,74 @@ public: size_t max_vertex_count () const; /** - * @brief Names a layer + * @brief Register a layer under the given name * This is a formal name for the layer. Using a name or layer properties * (see below) enhances readability of backannotated information - * if layers are involved. Use this method or the other variants to - * attach a name or standard layer properties to a region delivered - * by "make_layer" or derived from other regions through boolean - * operations. + * if layers are involved. Use this method to attach a name to a region + * derived by boolean operations for example. + * Named regions are persisted inside the LayoutToNetlist object. Only + * named regions can be put into "connect". */ - void name (const db::Region ®ion, const std::string &name); + void register_layer (const db::Region ®ion, const std::string &name); /** - * @brief Gets the name of the given layer + * @brief Gets the name of the given region + * Returns an empty string if the region does not have a name. */ - std::string name (const db::Region ®ion) const + std::string name (const db::Region ®ion) const; + + /** + * @brief Gets the name of the given layer by index + * Returns an empty string if the layer does not have a name. + */ + std::string name (unsigned int) const; + + /** + * @brief Returns true, if the region is a persisted region + * Persisted regions have a name and are kept inside the LayoutToNetlist + * object. + */ + bool is_persisted (const db::Region ®ion) const; + + /** + * @brief Gets the region (layer) with the given name + * If the name is not valid, this method returns 0. Otherwise it + * will return a new'd Region object referencing the layer with + * the given name. It must be deleted by the caller. + */ + db::Region *layer_by_name (const std::string &name); + + /** + * @brief Gets the region (layer) by index + * If the index is not valid, this method returns 0. Otherwise it + * will return a new'd Region object referencing the layer with + * the given name. It must be deleted by the caller. + * Only named layers are managed by LayoutToNetlist and can + * be retrieved with this method. + */ + db::Region *layer_by_index (unsigned int index); + + /** + * @brief Iterates over the layer indexes and names managed by this object (begin) + */ + layer_iterator begin_layers () const { - return internal_layout ()->get_properties (layer_of (region)).name; + return m_name_of_layer.begin (); + } + + /** + * @brief Iterates over the layer indexes and names managed by this object (end) + */ + layer_iterator end_layers () const + { + return m_name_of_layer.end (); } /** * @brief Creates a new empty region + * This method returns a new'd object which must be deleted by the caller. */ - db::Region *make_layer (const std::string &n); + db::Region *make_layer (const std::string &name = std::string ()); /** * @brief Creates a new region representing an original layer @@ -144,6 +197,11 @@ public: * This variant produces polygons and takes texts for net name annotation. * A variant not taking texts is "make_polygon_layer". A Variant only taking * texts is "make_text_layer". + * All variants return a new'd object which must be deleted by the caller. + * Named regions are considered "precious". The LayoutToNetlist object will + * keep a reference on all named layers, so they persist during the lifetime + * of the LayoutToNetlist object. + * Only named layers can be used for connect (see below). */ db::Region *make_layer (unsigned int layer_index, const std::string &name = std::string ()); @@ -179,18 +237,21 @@ public: * a derived layer. Certain limitations apply. It's safe to use * boolean operations for deriving layers. Other operations are applicable as long as they are * capable of delivering hierarchical layers. + * Regions put into "connect" need to be named. */ void connect (const db::Region &l); /** * @brief Defines an inter-layer connection for the given layers. * The conditions mentioned with intra-layer "connect" apply for this method too. + * Regions put into "connect" need to be named. */ void connect (const db::Region &a, const db::Region &b); /** * @brief Connects the given layer with a global net with the given name * Returns the global net ID + * Regions put into "connect" need to be named. */ size_t connect_global (const db::Region &l, const std::string &gn); @@ -237,6 +298,11 @@ public: */ db::Cell *internal_top_cell (); + /** + * @brief Ensures the internal layout is made + */ + void ensure_internal_layout (); + /** * @brief Gets the connectivity object */ @@ -406,8 +472,12 @@ private: db::hier_clusters m_net_clusters; std::auto_ptr mp_netlist; std::set m_dlrefs; + std::map m_named_regions; + std::map m_name_of_layer; bool m_netlist_extracted; + db::DeepLayer m_dummy_layer; + void init (); size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path); void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const Net *net, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index cc051af0b..efa35d487 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -29,46 +29,6 @@ namespace db namespace l2n_std_reader { -class Layers -{ -public: - Layers () { } - - ~Layers () - { - for (std::map::const_iterator i = m_layers.begin (); i != m_layers.end (); ++i) { - delete i->second; - } - m_layers.clear (); - } - - void add (const std::string &name, db::Region *region) - { - if (m_layers.find (name) != m_layers.end ()) { - delete region; - throw tl::Exception (tl::to_string (tr ("Duplicate layer name: ")) + name); - } - m_layers.insert (std::make_pair (name, region)); - } - - bool has_layer (const std::string &name) const - { - return m_layers.find (name) != m_layers.end (); - } - - db::Region &layer (const std::string &name) - { - std::map::const_iterator l = m_layers.find (name); - if (l == m_layers.end ()) { - throw tl::Exception (tl::to_string (tr ("Not a valid layer name: ")) + name); - } - return *l->second; - } - -private: - std::map m_layers; -}; - class Brace { public: @@ -185,9 +145,13 @@ void LayoutToNetlistStandardReader::read (db::LayoutToNetlist *l2n) } } -const db::Region *LayoutToNetlistStandardReader::layer_by_name (const std::string &name) +static db::Region &layer_by_name (db::LayoutToNetlist *l2n, const std::string &name) { - return mp_layers->has_layer (name) ? &mp_layers->layer (name) : 0; + db::Region *l = l2n->layer_by_name (name); + if (! l) { + throw tl::Exception (tl::to_string (tr ("Not a valid layer name: ")) + name); + } + return *l; } void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) @@ -196,13 +160,17 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) std::string description; // TODO: there probably is a more efficient way to force the layout inside l2n to be made - l2n->make_layer (std::string ()); + l2n->ensure_internal_layout (); tl_assert (l2n->internal_layout ()); + l2n->internal_layout ()->dbu (1.0); // mainly for testing + + if (l2n->internal_layout ()->cells () == 0) { + l2n->internal_layout ()->add_cell ("TOP"); + } + tl_assert (l2n->internal_top_cell () != 0); l2n->make_netlist (); - mp_layers.reset (new Layers ()); - while (! at_end ()) { if (test (skeys::version_key) || test (lkeys::version_key)) { @@ -237,7 +205,7 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) Brace br (this); std::string layer; read_word_or_quoted (layer); - mp_layers->add (layer, l2n->make_layer (layer)); + delete l2n->make_layer (layer); br.done (); } else if (test (skeys::connect_key) || test (lkeys::connect_key)) { @@ -248,7 +216,7 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) while (br) { std::string l2; read_word_or_quoted (l2); - l2n->connect (mp_layers->layer (l1), mp_layers->layer (l2)); + l2n->connect (layer_by_name (l2n, l1), layer_by_name (l2n, l2)); } br.done (); @@ -260,7 +228,7 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) while (br) { std::string g; read_word_or_quoted (g); - l2n->connect_global (mp_layers->layer (l1), g); + l2n->connect_global (layer_by_name (l2n, l1), g); } br.done (); @@ -377,7 +345,7 @@ LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n) Brace br (this); read_word_or_quoted (lname); - unsigned int lid = l2n->layer_of (mp_layers->layer (lname)); + unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname)); db::Coord l = read_coord (); db::Coord b = read_coord (); @@ -394,7 +362,7 @@ LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n) Brace br (this); read_word_or_quoted (lname); - unsigned int lid = l2n->layer_of (mp_layers->layer (lname)); + unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname)); std::vector pt; diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h index 276614197..2c0d7e996 100644 --- a/src/db/db/dbLayoutToNetlistReader.h +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -65,8 +65,6 @@ public: void read (db::LayoutToNetlist *l2n); - const db::Region *layer_by_name (const std::string &name); - private: friend class l2n_std_reader::Brace; typedef l2n_std_reader::Brace Brace; @@ -85,7 +83,6 @@ private: std::string m_path; std::string m_line; tl::Extractor m_ex; - std::auto_ptr mp_layers; void do_read (db::LayoutToNetlist *l2n); diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 437ca16d8..7d04696ff 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -63,14 +63,13 @@ std_writer_impl::std_writer_impl (tl::OutputStream &stream) // .. nothing yet .. } -static std::string name_for_layer (const db::Layout *layout, unsigned int l) +static std::string name_for_layer (const db::LayoutToNetlist *l2n, unsigned int l) { - const db::LayerProperties &lp = layout->get_properties (l); - if (lp.is_named ()) { - return tl::to_word_or_quoted_string (lp.name); - } else { - return "L" + tl::to_string (l); + std::string n = l2n->name (l); + if (n.empty ()) { + n = "L" + tl::to_string (l); } + return n; } template @@ -104,7 +103,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) *mp_stream << endl << "# Mask layers" << endl; } for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { - *mp_stream << Keys::layer_key << "(" << name_for_layer (ly, *l) << ")" << endl; + *mp_stream << Keys::layer_key << "(" << name_for_layer (l2n, *l) << ")" << endl; } if (! Keys::is_short ()) { @@ -115,9 +114,9 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l); db::Connectivity::layer_iterator cb = l2n->connectivity ().begin_connected (*l); if (cb != ce) { - *mp_stream << Keys::connect_key << "(" << name_for_layer (ly, *l); + *mp_stream << Keys::connect_key << "(" << name_for_layer (l2n, *l); for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) { - *mp_stream << " " << name_for_layer (ly, *c); + *mp_stream << " " << name_for_layer (l2n, *c); } *mp_stream << ")" << endl; } @@ -136,7 +135,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) } any = true; } - *mp_stream << Keys::global_key << "(" << name_for_layer (ly, *l); + *mp_stream << Keys::global_key << "(" << name_for_layer (l2n, *l); for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) { *mp_stream << " " << tl::to_word_or_quoted_string (l2n->connectivity ().global_net_name (*g)); } @@ -260,7 +259,6 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before extraction has been done"))); } - const db::Layout *ly = l2n->internal_layout (); const db::hier_clusters &clusters = l2n->net_clusters (); const db::Circuit *circuit = net.circuit (); const db::Connectivity &conn = l2n->connectivity (); @@ -290,7 +288,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net } *mp_stream << indent2; - write (si.operator-> (), si.trans (), name_for_layer (ly, *l)); + write (si.operator-> (), si.trans (), name_for_layer (l2n, *l)); *mp_stream << endl; prev_ci = ci; @@ -365,7 +363,6 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Dev { const std::vector &td = device_model.device_class ()->terminal_definitions (); - const db::Layout *ly = l2n->internal_layout (); const db::hier_clusters &clusters = l2n->net_clusters (); const db::Connectivity &conn = l2n->connectivity (); @@ -379,7 +376,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Dev for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { *mp_stream << indent2; - write (s.operator-> (), db::ICplxTrans (), name_for_layer (ly, *l)); + write (s.operator-> (), db::ICplxTrans (), name_for_layer (l2n, *l)); *mp_stream << endl; } diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 1165bae84..ec3c0ba08 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -502,12 +502,36 @@ public: Region &operator= (const Region &other); /** - * @brief Constructor from an object - * - * Creates a region representing a single instance of that object + * @brief Constructor from a box */ - template - Region (const Sh &s) + explicit Region (const db::Box &s) + : mp_delegate (0) + { + insert (s); + } + + /** + * @brief Constructor from a polygon + */ + explicit Region (const db::Polygon &s) + : mp_delegate (0) + { + insert (s); + } + + /** + * @brief Constructor from a simple polygon + */ + explicit Region (const db::SimplePolygon &s) + : mp_delegate (0) + { + insert (s); + } + + /** + * @brief Constructor from a path + */ + explicit Region (const db::Path &s) : mp_delegate (0) { insert (s); @@ -521,7 +545,7 @@ public: * style. */ template - Region (const Iter &b, const Iter &e) + explicit Region (const Iter &b, const Iter &e) : mp_delegate (0) { reserve (e - b); @@ -536,7 +560,7 @@ public: * Creates a region from a recursive shape iterator. This allows feeding a region * from a hierarchy of cells. */ - Region (const RecursiveShapeIterator &si); + explicit Region (const RecursiveShapeIterator &si); /** * @brief Constructor from a RecursiveShapeIterator with a transformation @@ -545,7 +569,7 @@ public: * from a hierarchy of cells. The transformation is useful to scale to a specific * DBU for example. */ - Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); + explicit Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); /** * @brief Constructor from a RecursiveShapeIterator providing a deep representation @@ -556,12 +580,12 @@ public: * "area_ratio" and "max_vertex_count" are optimization parameters for the * shape splitting algorithm. */ - Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio = 3.0, size_t max_vertex_count = 16); + explicit Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio = 3.0, size_t max_vertex_count = 16); /** * @brief Constructor from a RecursiveShapeIterator providing a deep representation with transformation */ - Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 3.0, size_t max_vertex_count = 16); + explicit Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 3.0, size_t max_vertex_count = 16); /** * @brief Gets the underlying delegate object diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index c8c0eb80d..7fc2e642b 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -23,6 +23,7 @@ #include "gsiDecl.h" #include "dbLayoutToNetlist.h" #include "dbLayoutToNetlistWriter.h" +#include "dbLayoutToNetlistReader.h" #include "tlStream.h" namespace gsi @@ -33,6 +34,11 @@ static db::LayoutToNetlist *make_l2n (const db::RecursiveShapeIterator &iter) return new db::LayoutToNetlist (iter); } +static db::LayoutToNetlist *make_l2n_default () +{ + return new db::LayoutToNetlist (); +} + static db::Layout *l2n_internal_layout (db::LayoutToNetlist *l2n) { // although this isn't very clean, we dare to do so as const references are pretty useless in script languages. @@ -67,10 +73,31 @@ static void write_l2n (const db::LayoutToNetlist *l2n, const std::string &path, writer.write (l2n); } +static void read_l2n (db::LayoutToNetlist *l2n, const std::string &path) +{ + tl::InputStream stream (path); + db::LayoutToNetlistStandardReader reader (stream); + reader.read (l2n); +} + +static std::vector l2n_layer_names (const db::LayoutToNetlist *l2n) +{ + std::vector ln; + for (db::LayoutToNetlist::layer_iterator l = l2n->begin_layers (); l != l2n->end_layers (); ++l) { + ln.push_back (l->second); + } + return ln; +} + Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::constructor ("new", &make_l2n, gsi::arg ("iter"), - "@brief The constructor\n" - "See the class description for details.\n" + "@brief Creates a new extractor connected to an original layout\n" + "This constructor will attach the extractor to an original layout through the " + "shape iterator.\n" + ) + + gsi::constructor ("new", &make_l2n_default, + "@brief Creates a new and empty extractor object\n" + "The main objective for this constructor is to create an object suitable for reading an annotated netlist.\n" ) + gsi::method ("threads=", &db::LayoutToNetlist::set_threads, gsi::arg ("n"), "@brief Sets the number of threads to use for operations which support multiple threads\n" @@ -95,34 +122,65 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("max_vertex_count", &db::LayoutToNetlist::max_vertex_count, "See \\max_vertex_count= for details about this attribute." ) + - gsi::method ("name", &db::LayoutToNetlist::name, gsi::arg ("l"), + gsi::method ("name", (std::string (db::LayoutToNetlist::*) (const db::Region ®ion) const) &db::LayoutToNetlist::name, gsi::arg ("l"), + "@brief Get the name of the given layer\n" + ) + + gsi::method ("name", (std::string (db::LayoutToNetlist::*) (unsigned int) const) &db::LayoutToNetlist::name, gsi::arg ("l"), + "@brief Get the name of the given layer (by index)\n" + ) + + gsi::method ("register", (void (db::LayoutToNetlist::*) (const db::Region ®ion, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n"), "@brief Names the given layer\n" "'l' must be a hierarchical region derived with \\make_layer, \\make_text_layer or \\make_polygon_layer or " "a region derived from those by boolean operations or other hierarchical operations.\n" "\n" "Naming a layer allows the system to indicate the layer in various contexts, i.e. " - "when writing the data to a file.\n" + "when writing the data to a file. Named layers are also persisted inside the LayoutToNetlist object. " + "They are not discarded when the Region object is destroyed. Only named layers can be put into " + "\\connect.\n" ) + - gsi::method ("make_layer", &db::LayoutToNetlist::make_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), + gsi::method_ext ("layer_names", &l2n_layer_names, + "@brief Returns a list of names of the layer kept inside the LayoutToNetlist object." + ) + + gsi::factory ("layer_by_name", &db::LayoutToNetlist::layer_by_name, gsi::arg ("name"), + "@brief Gets a layer object for the given name.\n" + "The returned object is a copy which represents the named layer." + ) + + gsi::factory ("layer_by_index", &db::LayoutToNetlist::layer_by_index, gsi::arg ("index"), + "@brief Gets a layer object for the given index.\n" + "Only named layers can be retrieved with this method. " + "The returned object is a copy which represents the named layer." + ) + + gsi::method ("is_persisted?", &db::LayoutToNetlist::is_persisted, gsi::arg ("layer"), + "@brief Returns true, if the given layer is a persisted region.\n" + "Persisted layers are kept inside the LayoutToNetlist object and are not released " + "if their object is destroyed. Named layers are persisted, unnamed layers are not. " + "Only persisted, named layers can be put into \\connect." + ) + + gsi::factory ("make_layer", (db::Region *(db::LayoutToNetlist::*) (const std::string &)) &db::LayoutToNetlist::make_layer, gsi::arg ("name", std::string ()), + "@brief Creates a new, empty hierarchical region\n" + "\n" + "The name is optional. If given, the layer will already be named accordingly (see \\register).\n" + ) + + gsi::factory ("make_layer", (db::Region *(db::LayoutToNetlist::*) (unsigned int, const std::string &)) &db::LayoutToNetlist::make_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), "@brief Creates a new hierarchical region representing an original layer\n" "'layer_index' is the layer index of the desired layer in the original layout.\n" "This variant produces polygons and takes texts for net name annotation.\n" "A variant not taking texts is \\make_polygon_layer. A Variant only taking\n" "texts is \\make_text_layer.\n" "\n" - "The name is optional. If given, the layer will already be named accordingly (see \\name).\n" + "The name is optional. If given, the layer will already be named accordingly (see \\register).\n" ) + - gsi::method ("make_text_layer", &db::LayoutToNetlist::make_text_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), + gsi::factory ("make_text_layer", &db::LayoutToNetlist::make_text_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), "@brief Creates a new region representing an original layer taking texts only\n" "See \\make_layer for details.\n" "\n" - "The name is optional. If given, the layer will already be named accordingly (see \\name).\n" + "The name is optional. If given, the layer will already be named accordingly (see \\register).\n" ) + - gsi::method ("make_polygon_layer", &db::LayoutToNetlist::make_polygon_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), + gsi::factory ("make_polygon_layer", &db::LayoutToNetlist::make_polygon_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), "@brief Creates a new region representing an original layer taking polygons and texts\n" "See \\make_layer for details.\n" "\n" - "The name is optional. If given, the layer will already be named accordingly (see \\name).\n" + "The name is optional. If given, the layer will already be named accordingly (see \\register).\n" ) + gsi::method ("extract_devices", &db::LayoutToNetlist::extract_devices, gsi::arg ("extractor"), gsi::arg ("layers"), "@brief Extracts devices\n" @@ -280,6 +338,10 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method_ext ("write", &write_l2n, gsi::arg ("path"), gsi::arg ("short_format", false), "@brief Writes the extracted netlist to a file.\n" "This method employs the native format of KLayout.\n" + ) + + gsi::method_ext ("read", &read_l2n, gsi::arg ("path"), + "@brief Reads the extracted netlist from the file.\n" + "This method employs the native format of KLayout.\n" ), "@brief A generic framework for extracting netlists from layouts\n" "\n" diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc index 3892eecf4..c5eb117ca 100644 --- a/src/db/unit_tests/dbHierarchyBuilderTests.cc +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -59,6 +59,41 @@ TEST(1) db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au1.gds"); } +TEST(1_WithEmptyLayer) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + + db::Layout target; + db::HierarchyBuilder builder (&target); + + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), std::set ()); + iter.push (&builder); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + + iter.push (&builder); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au1.gds"); +} + TEST(2_WithoutClip) { db::Layout ly; @@ -251,6 +286,56 @@ TEST(2_WithEmptyResult) db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2e.gds"); } +TEST(2_WithClipAndSimplificationAndEmptyLayer) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ReducingHierarchyBuilderShapeReceiver red(0, 1.2, 4); + db::ClippingHierarchyBuilderShapeReceiver clip(&red); + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + db::Box clip_box (5000, -2000, 18500, 6000); + + builder.set_target_layer (target.insert_layer (db::LayerProperties (100, 0))); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), std::set (), clip_box); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, clip_box); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2f.gds"); +} + TEST(3_ComplexRegionWithClip) { db::Layout ly; diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc index 83aaeab10..c7dc80031 100644 --- a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -34,10 +34,7 @@ TEST(1_ReaderBasic) { - db::Layout ly; - - db::Cell &tc = ly.cell (ly.add_cell ("TOP")); - db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + db::LayoutToNetlist l2n; std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt"); tl::InputStream is_in (in_path); @@ -69,20 +66,20 @@ TEST(1_ReaderBasic) { db::Layout ly2; - ly2.dbu (ly.dbu ()); + ly2.dbu (l2n.internal_layout ()->dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); std::map lmap; - lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = reader.layer_by_name ("psd"); - lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = reader.layer_by_name ("nsd"); - lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = reader.layer_by_name ("poly"); - lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = reader.layer_by_name ("diff_cont"); - lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = reader.layer_by_name ("poly_cont"); - lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = reader.layer_by_name ("metal1"); - lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = reader.layer_by_name ("via1"); - lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = reader.layer_by_name ("metal2"); + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd"); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd"); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_by_name ("poly"); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_by_name ("diff_cont"); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_by_name ("poly_cont"); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_by_name ("metal1"); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1"); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2"); l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, "DEVICE_"); @@ -97,10 +94,7 @@ TEST(1_ReaderBasic) TEST(1b_ReaderBasicShort) { - db::Layout ly; - - db::Cell &tc = ly.cell (ly.add_cell ("TOP")); - db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + db::LayoutToNetlist l2n; std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_s.txt"); tl::InputStream is_in (in_path); @@ -132,10 +126,7 @@ TEST(1b_ReaderBasicShort) TEST(2_ReaderWithGlobalNets) { - db::Layout ly; - - db::Cell &tc = ly.cell (ly.add_cell ("TOP")); - db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + db::LayoutToNetlist l2n; std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2.txt"); tl::InputStream is_in (in_path); @@ -167,24 +158,24 @@ TEST(2_ReaderWithGlobalNets) { db::Layout ly2; - ly2.dbu (ly.dbu ()); + ly2.dbu (l2n.internal_layout ()->dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); std::map lmap; - lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = reader.layer_by_name ("psd"); - lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = reader.layer_by_name ("nsd"); - lmap [ly2.insert_layer (db::LayerProperties (12, 0))] = reader.layer_by_name ("rbulk"); - lmap [ly2.insert_layer (db::LayerProperties (13, 0))] = reader.layer_by_name ("ptie"); - lmap [ly2.insert_layer (db::LayerProperties (14, 0))] = reader.layer_by_name ("ntie"); - lmap [ly2.insert_layer (db::LayerProperties (1, 0)) ] = reader.layer_by_name ("nwell"); - lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = reader.layer_by_name ("poly"); - lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = reader.layer_by_name ("diff_cont"); - lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = reader.layer_by_name ("poly_cont"); - lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = reader.layer_by_name ("metal1"); - lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = reader.layer_by_name ("via1"); - lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = reader.layer_by_name ("metal2"); + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd"); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd"); + lmap [ly2.insert_layer (db::LayerProperties (12, 0))] = l2n.layer_by_name ("rbulk"); + lmap [ly2.insert_layer (db::LayerProperties (13, 0))] = l2n.layer_by_name ("ptie"); + lmap [ly2.insert_layer (db::LayerProperties (14, 0))] = l2n.layer_by_name ("ntie"); + lmap [ly2.insert_layer (db::LayerProperties (1, 0)) ] = l2n.layer_by_name ("nwell"); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_by_name ("poly"); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_by_name ("diff_cont"); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_by_name ("poly_cont"); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_by_name ("metal1"); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1"); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2"); l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_"); diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index cc358fee1..e063dab20 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -122,7 +122,52 @@ static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_la return lid; } -TEST(1_Basic) +TEST(0_Basic) +{ + db::LayoutToNetlist l2n; + + std::auto_ptr reg (l2n.make_layer ("l1")); + EXPECT_EQ (l2n.is_persisted (*reg), true); + EXPECT_EQ (l2n.name (*reg), "l1"); + EXPECT_EQ (l2n.layer_of (*reg), 0u); + EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (0), true); + reg.reset (0); + EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (0), true); + EXPECT_EQ (l2n.name (0u), "l1"); + + EXPECT_EQ (l2n.layer_by_index (1) == 0, true); + EXPECT_EQ (l2n.layer_by_name ("l2") == 0, true); + + std::auto_ptr reg_copy (l2n.layer_by_name ("l1")); + EXPECT_EQ (reg_copy.get () != 0, true); + EXPECT_EQ (l2n.name (*reg_copy), "l1"); + EXPECT_EQ (l2n.layer_of (*reg_copy), 0u); + reg_copy.reset (l2n.layer_by_index (0)); + EXPECT_EQ (reg_copy.get () != 0, true); + EXPECT_EQ (l2n.name (*reg_copy), "l1"); + EXPECT_EQ (l2n.layer_of (*reg_copy), 0u); + reg_copy.reset (0); + + std::auto_ptr reg2 (l2n.make_layer ()); + EXPECT_EQ (l2n.name (1u), ""); + EXPECT_EQ (l2n.name (*reg2), ""); + EXPECT_EQ (l2n.layer_of (*reg2), 1u); + EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (1), true); + reg2.reset (0); + EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (1), false); + + std::auto_ptr reg3 (l2n.make_layer ("l3")); + EXPECT_EQ (l2n.name (*reg3), "l3"); + EXPECT_EQ (l2n.layer_of (*reg3), 1u); + + std::string s; + for (db::LayoutToNetlist::layer_iterator l = l2n.begin_layers (); l != l2n.end_layers (); ++l) { + s += tl::to_string (l->first) + ":" + l->second + ";"; + } + EXPECT_EQ (s, "0:l1;1:l3;"); +} + +TEST(1_BasicExtraction) { db::Layout ly; db::LayerMap lmap; @@ -157,17 +202,17 @@ TEST(1_Basic) db::Cell &tc = ly.cell (*ly.begin_top_down ()); db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); - std::auto_ptr rnwell (l2n.make_layer (nwell)); - std::auto_ptr ractive (l2n.make_layer (active)); - std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); - std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); - std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); - std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); - std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); - std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); - std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); - std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); - std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); // derived regions @@ -215,6 +260,9 @@ TEST(1_Basic) // net extraction + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + // Intra-layer l2n.connect (rpsd); l2n.connect (rnsd); @@ -513,17 +561,17 @@ TEST(2_Probing) db::Cell &tc = ly.cell (*ly.begin_top_down ()); db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); - std::auto_ptr rnwell (l2n.make_layer (nwell)); - std::auto_ptr ractive (l2n.make_layer (active)); - std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); - std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); - std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); - std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); - std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); - std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); - std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); - std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); - std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); // derived regions @@ -568,6 +616,9 @@ TEST(2_Probing) // net extraction + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + // Intra-layer l2n.connect (rpsd); l2n.connect (rnsd); @@ -754,19 +805,19 @@ TEST(3_GlobalNetConnections) db::Cell &tc = ly.cell (*ly.begin_top_down ()); db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); - std::auto_ptr rnwell (l2n.make_layer (nwell)); - std::auto_ptr ractive (l2n.make_layer (active)); - std::auto_ptr rpplus (l2n.make_layer (pplus)); - std::auto_ptr rnplus (l2n.make_layer (nplus)); - std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); - std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); - std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); - std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); - std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); - std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); - std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); - std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); - std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); // derived regions @@ -819,6 +870,11 @@ TEST(3_GlobalNetConnections) // net extraction + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rntie, "ntie"); + // Intra-layer l2n.connect (rpsd); l2n.connect (rnsd); @@ -1021,20 +1077,20 @@ TEST(4_GlobalNetDeviceExtraction) db::Cell &tc = ly.cell (*ly.begin_top_down ()); db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); - std::auto_ptr rbulk (l2n.make_layer (ly.insert_layer ())); - std::auto_ptr rnwell (l2n.make_layer (nwell)); - std::auto_ptr ractive (l2n.make_layer (active)); - std::auto_ptr rpplus (l2n.make_layer (pplus)); - std::auto_ptr rnplus (l2n.make_layer (nplus)); - std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); - std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); - std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); - std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); - std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); - std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); - std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); - std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); - std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + std::auto_ptr rbulk (l2n.make_layer (ly.insert_layer (), "bulk")); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); // derived regions @@ -1089,6 +1145,11 @@ TEST(4_GlobalNetDeviceExtraction) // net extraction + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rntie, "ntie"); + // Intra-layer l2n.connect (rpsd); l2n.connect (rnsd); @@ -1294,20 +1355,20 @@ TEST(5_DeviceExtractionWithDeviceCombination) db::Cell &tc = ly.cell (*ly.begin_top_down ()); db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); - std::auto_ptr rbulk (l2n.make_layer (ly.insert_layer ())); - std::auto_ptr rnwell (l2n.make_layer (nwell)); - std::auto_ptr ractive (l2n.make_layer (active)); - std::auto_ptr rpplus (l2n.make_layer (pplus)); - std::auto_ptr rnplus (l2n.make_layer (nplus)); - std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); - std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); - std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); - std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); - std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); - std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); - std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); - std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); - std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + std::auto_ptr rbulk (l2n.make_layer ("bulk")); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); // derived regions @@ -1362,6 +1423,11 @@ TEST(5_DeviceExtractionWithDeviceCombination) // net extraction + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rntie, "ntie"); + // Intra-layer l2n.connect (rpsd); l2n.connect (rnsd); diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 02d81d3e3..426e69980 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -90,16 +90,16 @@ TEST(1_WriterBasic) db::Region rpactive = *ractive & *rnwell; db::Region rpgate = rpactive & *rpoly; db::Region rpsd = rpactive - rpgate; - l2n.name (rpactive, "pactive"); - l2n.name (rpgate, "pgate"); - l2n.name (rpsd, "psd"); + l2n.register_layer (rpactive, "pactive"); + l2n.register_layer (rpgate, "pgate"); + l2n.register_layer (rpsd, "psd"); db::Region rnactive = *ractive - *rnwell; db::Region rngate = rnactive & *rpoly; db::Region rnsd = rnactive - rngate; - l2n.name (rnactive, "nactive"); - l2n.name (rngate, "ngate"); - l2n.name (rnsd, "nsd"); + l2n.register_layer (rnactive, "nactive"); + l2n.register_layer (rngate, "ngate"); + l2n.register_layer (rnsd, "nsd"); db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); @@ -296,20 +296,20 @@ TEST(2_WriterWithGlobalNets) db::Region rntie = ractive_in_nwell & *rnplus; db::Region rpgate = rpactive & *rpoly; db::Region rpsd = rpactive - rpgate; - l2n.name (rpactive, "pactive"); - l2n.name (rntie, "ntie"); - l2n.name (rpgate, "pgate"); - l2n.name (rpsd, "psd"); + l2n.register_layer (rpactive, "pactive"); + l2n.register_layer (rntie, "ntie"); + l2n.register_layer (rpgate, "pgate"); + l2n.register_layer (rpsd, "psd"); db::Region ractive_outside_nwell = *ractive - *rnwell; db::Region rnactive = ractive_outside_nwell & *rnplus; db::Region rptie = ractive_outside_nwell & *rpplus; db::Region rngate = rnactive & *rpoly; db::Region rnsd = rnactive - rngate; - l2n.name (rnactive, "nactive"); - l2n.name (rptie, "ptie"); - l2n.name (rngate, "ngate"); - l2n.name (rnsd, "nsd"); + l2n.register_layer (rnactive, "nactive"); + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rngate, "ngate"); + l2n.register_layer (rnsd, "nsd"); // return the computed layers into the original layout and write it for debugging purposes diff --git a/testdata/algo/hierarchy_builder_au2f.gds b/testdata/algo/hierarchy_builder_au2f.gds new file mode 100644 index 0000000000000000000000000000000000000000..7d244fcf7b18358645b45ebc6d19f25a44b73952 GIT binary patch literal 11978 zcmeHM&1+Ow6u>92dN}&N*k0Yno0>m|A8*L5Gh5A&@NI} zU36u;kYeq^t_p>MS#{Bct1h}IC?a&@rXaXd%)I{2yZ6k!@4k2MyYn(?7Y&5paL=53 z&i$Q_`!OOGQS_lWREwH_i<%e{2gHPU$^0x@QG8?+j{53>Zy;;E}tG>EV2n3pQdvH*%l*@@4)6l&t`0{b9*aEx?d*A_LdN} zi}ab1$J4QD>zog>nT$)D$#~wTijCnjc)Bb76n9Av|JNCLJlN-69!%*xFl-So>=XOQ z$m7~ph_BI#^fg*Z^>{7S_?;`_H_y*NZO-;=Hq1QgMp(~n`~#C4v^I@A-n}jBMe$_m z!|x5!2S0P9uaU>aM#XxY3H14%@)=mk=X=U$pzU*h&G_zXWJh9h19+Y6OXP7kXQW4V zB-Z9g5B~~zJdm?q9dvXZSn<)Er15>xTy+&!z)D7x9ixM<(fxc--X zp6dA=nRUE<0Y1NuzIOrs-@#|9!xVX(uwor{LTthwEJ8f(`ijvi#>Tr|Hs-^*UnDS>7gx8EZC_IL^v6>_txb}q1h6x zgYn4^*0KJ8lL^m=vw@lsXCR(Yo;fgMd=Pen=0jzq9`+789o>1GW|q+d=$_1m*jpOMFR z9S!1)W1!DU)=Dj=E-9vfm6*agHf0Q0i7Du%OaUt~1-+ChV4qCovrG5gAv3!ke~w|`b0 zOd*dC9|`iAJDBeEmL>KGxd%@v5)yg5KTk=YJSObZ(;T}C4tBc_q;C|tBA=z)%`WMY zZ-neCBaa8NoAY@dK96A9QT>8}{U5eIK@=gQecvS?d6@Az^Ww4n2s=xRliwpDT6B~9 zhmpq#-TIHuq3f=rrvaOEYlc6JJRYx0{gS@I8HN7tT1n_iE&cfA+GTJ@=cU@$L{}kS z*0-J=Yw%)!$|{i9#`$nJBai!7hyQp3sn1Akd zI8%Ga!bJgw;dA?yq2;|m`fDE=T1r3A=Yo9R!7LoVXPpbw6{v!!>WeB!%3ZN|zPV6R zfw@@XkXszf`GLeTcorj;`HWZ~UoLy|Dp&VAn1y|5^=@N&e||cFJUE9t)C6{d^XRJI zF!DHICKodAj(A%}UCTQd)id327rJHD*!K^MRnHOS4=st)Y7;5B)rV+ zKA6S&7SEMuP2OQ?rS-#z(?#s@_{_-TK(m_}tU>Q5`F%p{HK{%$&7l6&>(`}Dox^RV})Mji(`^eJ07J2+bydAz@@$?gU+n*y^Y*8yf+9bj%C9UyoX z&Y%BtG0jy+F3B9^oU4+Hw2M)vA>9L-5fV49WG@GR{A zi{t{V7IV=V2p7S#7+mD5VTvQaqiP7Ag?%Z>tCoCmPWc;A_l)97D3G!Z#9m9S{5Gn< z`5m68#8>=cka;bWQ6IH$M-^gTc@FmcIWd4=8BedSy?1iFgYH$rmi{hY-K2l&*N^n} zK6VX}0fYM_i-?UWef6PF`o@w^I-PwNux5VizSa$00lncFD_D^44-Oxd)ZwF&I($@8 z)5pBs_b2;b0lkwMGtJ8tIAWlrj`b<2V|_~MSf7$w>qD=~)YUkCbwU+mm7eznA*%48 z-rn*S)4o<_(ZSc8Hy@PL!IzRc_)^kRz5;g3YJBMzAS*(XArW%$HS6U|NgaGCse>;i zE#)gh58G~Xa`?2UcQvn!IzRc_)^j`zR>>yyf^Dny$u+@BM7m76Voh7 zZ2kd$@zTl2TS*;!D`^?u=u!dRn|z1zrSEN7 zzQALEMF(F~UcQvn!IzRc_)^j`zEE`m-dEvE-*U2i$yc$8`$fmgmy$a8Qc?$BN?OJj zvfvr<)sg26oM$t91=+K*t0m_s zDL%I;&GhVyyrVxpJLAPyoyyajlN1)ok+oiZXl=k T^2$4tC300iib39q8V&I;d^qVc literal 0 HcmV?d00001 diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index 9ee4ab918..89e920d2b 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -61,7 +61,7 @@ class DBLayoutToNetlist_TestClass < TestBase assert_equal(l2n.internal_layout.cell(ci).name, ly2.cell(cm.cell_mapping(ci)).name) end - rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) ) + rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0), "metal1" ) bulk_id = l2n.connect_global(rmetal1, "BULK") assert_equal(l2n.global_net_name(bulk_id), "BULK") @@ -76,11 +76,11 @@ class DBLayoutToNetlist_TestClass < TestBase # only plain backend connectivity - rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) ) - rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1) ) - rvia1 = l2n.make_polygon_layer( ly.layer(7, 0) ) - rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0) ) - rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1) ) + rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0), "metal1" ) + rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1), "metal1_lbl" ) + rvia1 = l2n.make_polygon_layer( ly.layer(7, 0), "via1" ) + rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0), "metal2" ) + rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1), "metal2_lbl" ) # Intra-layer l2n.connect(rmetal1) @@ -141,19 +141,21 @@ END # only plain connectivity - ractive = l2n.make_layer( ly.layer(2, 0) ) - rpoly = l2n.make_polygon_layer( ly.layer(3, 0) ) - rpoly_lbl = l2n.make_text_layer( ly.layer(3, 1) ) - rdiff_cont = l2n.make_polygon_layer( ly.layer(4, 0) ) - rpoly_cont = l2n.make_polygon_layer( ly.layer(5, 0) ) - rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) ) - rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1) ) - rvia1 = l2n.make_polygon_layer( ly.layer(7, 0) ) - rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0) ) - rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1) ) + ractive = l2n.make_layer( ly.layer(2, 0), "active" ) + rpoly = l2n.make_polygon_layer( ly.layer(3, 0), "poly" ) + rpoly_lbl = l2n.make_text_layer( ly.layer(3, 1), "poly_lbl" ) + rdiff_cont = l2n.make_polygon_layer( ly.layer(4, 0), "diff_cont" ) + rpoly_cont = l2n.make_polygon_layer( ly.layer(5, 0), "poly_cont" ) + rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0), "metal1" ) + rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1), "metal1_lbl" ) + rvia1 = l2n.make_polygon_layer( ly.layer(7, 0), "via1" ) + rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0), "metal2" ) + rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1), "metal2_lbl" ) rsd = ractive - rpoly + l2n.register(rsd, "sd") + # Intra-layer l2n.connect(rsd) l2n.connect(rpoly) @@ -206,17 +208,17 @@ END l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, [])) - rnwell = l2n.make_layer( ly.layer(1, 0) ) - ractive = l2n.make_layer( ly.layer(2, 0) ) - rpoly = l2n.make_polygon_layer( ly.layer(3, 0) ) - rpoly_lbl = l2n.make_text_layer( ly.layer(3, 1) ) - rdiff_cont = l2n.make_polygon_layer( ly.layer(4, 0) ) - rpoly_cont = l2n.make_polygon_layer( ly.layer(5, 0) ) - rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) ) - rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1) ) - rvia1 = l2n.make_polygon_layer( ly.layer(7, 0) ) - rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0) ) - rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1) ) + rnwell = l2n.make_layer( ly.layer(1, 0), "nwell" ) + ractive = l2n.make_layer( ly.layer(2, 0), "active" ) + rpoly = l2n.make_polygon_layer( ly.layer(3, 0), "poly" ) + rpoly_lbl = l2n.make_text_layer( ly.layer(3, 1), "poly_lbl" ) + rdiff_cont = l2n.make_polygon_layer( ly.layer(4, 0), "diff_cont" ) + rpoly_cont = l2n.make_polygon_layer( ly.layer(5, 0), "poly_cont" ) + rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0), "metal1" ) + rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1), "metal1_lbl" ) + rvia1 = l2n.make_polygon_layer( ly.layer(7, 0), "via1" ) + rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0), "metal2" ) + rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1), "metal2_lbl" ) rpactive = ractive & rnwell rpgate = rpactive & rpoly @@ -236,6 +238,9 @@ END # Define connectivity for netlist extraction + l2n.register(rpsd, "psd") + l2n.register(rnsd, "nsd") + # Intra-layer l2n.connect(rpsd) l2n.connect(rnsd) @@ -297,20 +302,20 @@ END l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, [])) - rbulk = l2n.make_polygon_layer( ly.layer ) - rnwell = l2n.make_polygon_layer( ly.layer(1, 0) ) - ractive = l2n.make_polygon_layer( ly.layer(2, 0) ) - rpoly = l2n.make_polygon_layer( ly.layer(3, 0) ) - rpoly_lbl = l2n.make_text_layer( ly.layer(3, 1) ) - rdiff_cont = l2n.make_polygon_layer( ly.layer(4, 0) ) - rpoly_cont = l2n.make_polygon_layer( ly.layer(5, 0) ) - rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) ) - rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1) ) - rvia1 = l2n.make_polygon_layer( ly.layer(7, 0) ) - rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0) ) - rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1) ) - rpplus = l2n.make_polygon_layer( ly.layer(10, 0) ) - rnplus = l2n.make_polygon_layer( ly.layer(11, 0) ) + rbulk = l2n.make_layer( "bulk" ) + rnwell = l2n.make_polygon_layer( ly.layer(1, 0) , "nwell" ) + ractive = l2n.make_polygon_layer( ly.layer(2, 0) , "active" ) + rpoly = l2n.make_polygon_layer( ly.layer(3, 0) , "poly" ) + rpoly_lbl = l2n.make_text_layer( ly.layer(3, 1) , "poly_lbl" ) + rdiff_cont = l2n.make_polygon_layer( ly.layer(4, 0) , "diff_cont" ) + rpoly_cont = l2n.make_polygon_layer( ly.layer(5, 0) , "poly_cont" ) + rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) , "metal1" ) + rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1) , "metal1_lbl" ) + rvia1 = l2n.make_polygon_layer( ly.layer(7, 0) , "via1" ) + rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0) , "metal2" ) + rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1) , "metal2_lbl" ) + rpplus = l2n.make_polygon_layer( ly.layer(10, 0) , "pplus" ) + rnplus = l2n.make_polygon_layer( ly.layer(11, 0) , "nplus" ) ractive_in_nwell = ractive & rnwell rpactive = ractive_in_nwell & rpplus @@ -334,6 +339,11 @@ END # Define connectivity for netlist extraction + l2n.register(rpsd, "psd") + l2n.register(rnsd, "nsd") + l2n.register(rptie, "ptie") + l2n.register(rntie, "ntie") + # Intra-layer l2n.connect(rpsd) l2n.connect(rnsd) @@ -417,6 +427,24 @@ END end + def test_13_ReadAndWrite + + l2n = RBA::LayoutToNetlist::new + + input = File.join($ut_testsrc, "testdata", "algo", "l2n_writer_au.txt") + l2n.read(input) + + tmp = File::join($ut_testtmp, "tmp.txt") + l2n.write(tmp) + + assert_equal(File.open(tmp, "r").read, File.open(input, "r").read) + + assert_equal(l2n.layer_names.join(","), "poly,poly_lbl,diff_cont,poly_cont,metal1,metal1_lbl,via1,metal2,metal2_lbl,psd,nsd") + assert_equal(l2n.name(l2n.layer_by_name("metal1")), "metal1") + assert_equal(l2n.name(l2n.layer_by_index(l2n.layer_of(l2n.layer_by_name("metal1")))), "metal1") + + end + end load("test_epilogue.rb") From d79a448eaa9695571845578610ffb34a25db6965 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 21 Jan 2019 22:37:02 +0100 Subject: [PATCH 205/335] Some performance improvement by eliminating empty objects in the box scanner. --- src/db/db/dbBoxScanner.h | 55 +++++++++++++++++++ .../{dbDeviceModel.cc => dbDeviceAbstract.cc} | 0 .../{dbDeviceModel.h => dbDeviceAbstract.h} | 0 src/db/unit_tests/dbBoxScanner.cc | 53 ++++++++++++++++++ 4 files changed, 108 insertions(+) rename src/db/db/{dbDeviceModel.cc => dbDeviceAbstract.cc} (100%) rename src/db/db/{dbDeviceModel.h => dbDeviceAbstract.h} (100%) diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index 0f8f206f1..c350469ba 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -273,6 +273,25 @@ public: typedef bs_side_compare_vs_const_func > below_func; typedef bs_side_compare_vs_const_func > left_func; + // sort out the entries with an empty bbox (we must not put that into sort) + + typename container_type::iterator wi = m_pp.begin (); + for (typename container_type::iterator ri = m_pp.begin (); ri != m_pp.end (); ++ri) { + if (! bc (*ri->first).empty ()) { + if (wi != ri) { + *wi = *ri; + } + ++wi; + } else { + // we call finish on empty elements though + rec.finish (ri->first, ri->second); + } + } + + if (wi != m_pp.end ()) { + m_pp.erase (wi, m_pp.end ()); + } + if (m_pp.size () <= m_scanner_thr) { // below m_scanner_thr elements use the brute force approach which is faster in that case @@ -606,6 +625,42 @@ public: typedef bs_side_compare_vs_const_func > below_func2; typedef bs_side_compare_vs_const_func > left_func2; + // sort out the entries with an empty bbox (we must not put that into sort) + + typename container_type1::iterator wi1 = m_pp1.begin (); + for (typename container_type1::iterator ri1 = m_pp1.begin (); ri1 != m_pp1.end (); ++ri1) { + if (! bc1 (*ri1->first).empty ()) { + if (wi1 != ri1) { + *wi1 = *ri1; + } + ++wi1; + } else { + // we call finish on empty elements though + rec.finish1 (ri1->first, ri1->second); + } + } + + if (wi1 != m_pp1.end ()) { + m_pp1.erase (wi1, m_pp1.end ()); + } + + typename container_type2::iterator wi2 = m_pp2.begin (); + for (typename container_type2::iterator ri2 = m_pp2.begin (); ri2 != m_pp2.end (); ++ri2) { + if (! bc2 (*ri2->first).empty ()) { + if (wi2 != ri2) { + *wi2 = *ri2; + } + ++wi2; + } else { + // we call finish on empty elements though + rec.finish2 (ri2->first, ri2->second); + } + } + + if (wi2 != m_pp2.end ()) { + m_pp2.erase (wi2, m_pp2.end ()); + } + if (m_pp1.empty () || m_pp2.empty ()) { // trivial case diff --git a/src/db/db/dbDeviceModel.cc b/src/db/db/dbDeviceAbstract.cc similarity index 100% rename from src/db/db/dbDeviceModel.cc rename to src/db/db/dbDeviceAbstract.cc diff --git a/src/db/db/dbDeviceModel.h b/src/db/db/dbDeviceAbstract.h similarity index 100% rename from src/db/db/dbDeviceModel.h rename to src/db/db/dbDeviceAbstract.h diff --git a/src/db/unit_tests/dbBoxScanner.cc b/src/db/unit_tests/dbBoxScanner.cc index db4b660d2..8d396e3b7 100644 --- a/src/db/unit_tests/dbBoxScanner.cc +++ b/src/db/unit_tests/dbBoxScanner.cc @@ -283,6 +283,28 @@ TEST(1f) EXPECT_EQ (tr.str, ""); } +TEST(1g) +{ + // empty elements + db::box_scanner bs; + + std::vector bb; + bb.push_back (db::Box (0, 0, 101, 100)); + bb.push_back (db::Box (200, 0, 300, 100)); + bb.push_back (db::Box ()); + bb.push_back (db::Box (100, 0, 200, 100)); + bb.push_back (db::Box ()); + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert (&*b, b - bb.begin ()); + } + + BoxScannerTestRecorder tr; + bs.set_fill_factor (0.0); + db::box_convert bc; + bs.process (tr, 0, bc); + EXPECT_EQ (tr.str, "<2><4>(0-3)<0><1><3>"); +} + void run_test2 (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) { std::vector bb; @@ -985,6 +1007,37 @@ TEST(two_1b) EXPECT_EQ (trstop.str, "(1-12)"); } +TEST(two_1c) +{ + // some empty elements + db::box_scanner2 bs; + + std::vector bb; + bb.push_back (db::Box ()); + bb.push_back (db::Box (0, 0, 100, 100)); + bb.push_back (db::Box (100, 10, 200, 110)); + + std::vector bb2; + bb2.push_back (db::SimplePolygon (db::Box ())); + bb2.push_back (db::SimplePolygon (db::Box (50, 50, 150, 150))); + bb2.push_back (db::SimplePolygon (db::Box (10, 10, 110, 110))); + + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert1 (&*b, b - bb.begin ()); + } + for (std::vector::const_iterator b = bb2.begin (); b != bb2.end (); ++b) { + bs.insert2 (&*b, int (b - bb2.begin ()) + 10); + } + + BoxScannerTestRecorderTwo tr; + bs.set_fill_factor (0.0); + db::box_convert bc1; + db::box_convert bc2; + bs.set_scanner_threshold (0); + EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); + EXPECT_EQ (tr.str, "<0><10>(1-12)(2-12)(1-11)(2-11)<1><2><12><11>"); +} + void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) { std::vector bb; From 81bf47688effcc2841c0cd2e854e3e6a7ae95c95 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 21 Jan 2019 22:37:13 +0100 Subject: [PATCH 206/335] Renamed device model -> device abstract --- src/db/db/db.pro | 8 +- src/db/db/dbCircuit.cc | 8 +- src/db/db/dbCircuit.h | 2 +- src/db/db/dbDevice.cc | 12 +-- src/db/db/dbDevice.h | 18 ++--- src/db/db/dbDeviceAbstract.cc | 28 +++---- src/db/db/dbDeviceAbstract.h | 24 +++--- src/db/db/dbLayoutToNetlist.cc | 6 +- src/db/db/dbLayoutToNetlist.h | 4 +- src/db/db/dbLayoutToNetlistReader.cc | 14 ++-- src/db/db/dbLayoutToNetlistReader.h | 4 +- src/db/db/dbLayoutToNetlistWriter.cc | 18 ++--- src/db/db/dbNetlist.cc | 44 +++++------ src/db/db/dbNetlist.h | 82 ++++++++++---------- src/db/db/dbNetlistDeviceExtractor.cc | 10 +-- src/db/db/dbNetlistDeviceExtractor.h | 2 +- src/db/db/dbNetlistExtractor.cc | 10 +-- src/db/db/dbNetlistExtractor.h | 6 +- src/db/db/gsiDeclDbNetlist.cc | 24 +++--- src/db/unit_tests/dbNetlistExtractorTests.cc | 4 +- src/db/unit_tests/dbNetlistTests.cc | 40 +++++----- 21 files changed, 184 insertions(+), 184 deletions(-) diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 8ad80e277..0c9fcd68f 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -161,8 +161,8 @@ SOURCES = \ dbPin.cc \ dbLayoutToNetlistReader.cc \ dbLayoutToNetlistWriter.cc \ - dbDeviceModel.cc \ - dbLayoutToNetlistFormatDefs.cc + dbLayoutToNetlistFormatDefs.cc \ + dbDeviceAbstract.cc HEADERS = \ dbArray.h \ @@ -288,8 +288,8 @@ HEADERS = \ dbSubCircuit.h \ dbLayoutToNetlistReader.h \ dbLayoutToNetlistWriter.h \ - dbDeviceModel.h \ - dbLayoutToNetlistFormatDefs.h + dbLayoutToNetlistFormatDefs.h \ + dbDeviceAbstract.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index 4fff06e8c..d1d193158 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -330,13 +330,13 @@ void Circuit::translate_device_classes (const std::map &map) +void Circuit::translate_device_abstracts (const std::map &map) { for (device_iterator i = m_devices.begin (); i != m_devices.end (); ++i) { - if (i->device_model ()) { - std::map::const_iterator m = map.find (i->device_model ()); + if (i->device_abstract ()) { + std::map::const_iterator m = map.find (i->device_abstract ()); tl_assert (m != map.end ()); - i->set_device_model (m->second); + i->set_device_abstract (m->second); } } } diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index 14426b3f3..276ac520b 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -620,7 +620,7 @@ private: void translate_circuits (const std::map &map); void translate_device_classes (const std::map &map); - void translate_device_models (const std::map &map); + void translate_device_abstracts (const std::map &map); void set_netlist (Netlist *netlist); bool combine_parallel_devices (const db::DeviceClass &cls); bool combine_serial_devices (const db::DeviceClass &cls); diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc index e7a6b1cd0..e803cec08 100644 --- a/src/db/db/dbDevice.cc +++ b/src/db/db/dbDevice.cc @@ -31,7 +31,7 @@ namespace db // Device class implementation Device::Device () - : mp_device_class (0), mp_device_model (0), m_id (0), mp_circuit (0) + : mp_device_class (0), mp_device_abstract (0), m_id (0), mp_circuit (0) { // .. nothing yet .. } @@ -46,19 +46,19 @@ Device::~Device () } Device::Device (DeviceClass *device_class, const std::string &name) - : mp_device_class (device_class), mp_device_model (0), m_name (name), m_id (0), mp_circuit (0) + : mp_device_class (device_class), mp_device_abstract (0), m_name (name), m_id (0), mp_circuit (0) { // .. nothing yet .. } -Device::Device (DeviceClass *device_class, DeviceModel *device_model, const std::string &name) - : mp_device_class (device_class), mp_device_model (device_model), m_name (name), m_id (0), mp_circuit (0) +Device::Device (DeviceClass *device_class, DeviceAbstract *device_abstract, const std::string &name) + : mp_device_class (device_class), mp_device_abstract (device_abstract), m_name (name), m_id (0), mp_circuit (0) { // .. nothing yet .. } Device::Device (const Device &other) - : tl::Object (other), mp_device_class (0), mp_device_model (0), m_id (0), mp_circuit (0) + : tl::Object (other), mp_device_class (0), mp_device_abstract (0), m_id (0), mp_circuit (0) { operator= (other); } @@ -70,7 +70,7 @@ Device &Device::operator= (const Device &other) m_position = other.m_position; m_parameters = other.m_parameters; mp_device_class = other.mp_device_class; - mp_device_model = other.mp_device_model; + mp_device_abstract = other.mp_device_abstract; } return *this; } diff --git a/src/db/db/dbDevice.h b/src/db/db/dbDevice.h index 3a64f492a..5b12d25d5 100644 --- a/src/db/db/dbDevice.h +++ b/src/db/db/dbDevice.h @@ -36,7 +36,7 @@ namespace db class Circuit; class DeviceClass; -class DeviceModel; +class DeviceAbstract; /** * @brief An actual device @@ -67,7 +67,7 @@ public: /** * @brief The constructor */ - Device (DeviceClass *device_class, DeviceModel *device_model, const std::string &name = std::string ()); + Device (DeviceClass *device_class, DeviceAbstract *device_abstract, const std::string &name = std::string ()); /** * @brief Copy constructor @@ -101,19 +101,19 @@ public: } /** - * @brief Gets the device model + * @brief Gets the device abstract */ - const DeviceModel *device_model () const + const DeviceAbstract *device_abstract () const { - return mp_device_model; + return mp_device_abstract; } /** - * @brief Sets the device model + * @brief Sets the device abstract */ - void set_device_model (DeviceModel *dm) + void set_device_abstract (DeviceAbstract *dm) { - mp_device_model = dm; + mp_device_abstract = dm; } /** @@ -233,7 +233,7 @@ private: friend class Net; DeviceClass *mp_device_class; - DeviceModel *mp_device_model; + DeviceAbstract *mp_device_abstract; std::string m_name; db::DPoint m_position; std::vector m_terminal_refs; diff --git a/src/db/db/dbDeviceAbstract.cc b/src/db/db/dbDeviceAbstract.cc index a6d3b9d77..ea8d296cd 100644 --- a/src/db/db/dbDeviceAbstract.cc +++ b/src/db/db/dbDeviceAbstract.cc @@ -20,7 +20,7 @@ */ -#include "dbDeviceModel.h" +#include "dbDeviceAbstract.h" #include "dbCircuit.h" #include "dbNetlist.h" @@ -28,32 +28,32 @@ namespace db { // -------------------------------------------------------------------------------- -// DeviceModel class implementation +// DeviceAbstract class implementation -DeviceModel::DeviceModel () +DeviceAbstract::DeviceAbstract () : m_name (), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) { // .. nothing yet .. } -DeviceModel::~DeviceModel () +DeviceAbstract::~DeviceAbstract () { // .. nothing yet .. } -DeviceModel::DeviceModel (db::DeviceClass *device_class, const std::string &name) +DeviceAbstract::DeviceAbstract (db::DeviceClass *device_class, const std::string &name) : m_name (name), mp_device_class (device_class), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) { // .. nothing yet .. } -DeviceModel::DeviceModel (const DeviceModel &other) +DeviceAbstract::DeviceAbstract (const DeviceAbstract &other) : tl::Object (other), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) { operator= (other); } -DeviceModel &DeviceModel::operator= (const DeviceModel &other) +DeviceAbstract &DeviceAbstract::operator= (const DeviceAbstract &other) { if (this != &other) { m_name = other.m_name; @@ -64,33 +64,33 @@ DeviceModel &DeviceModel::operator= (const DeviceModel &other) return *this; } -void DeviceModel::set_netlist (Netlist *netlist) +void DeviceAbstract::set_netlist (Netlist *netlist) { mp_netlist = netlist; } -void DeviceModel::set_name (const std::string &n) +void DeviceAbstract::set_name (const std::string &n) { m_name = n; if (mp_netlist) { - mp_netlist->m_device_model_by_name.invalidate (); + mp_netlist->m_device_abstract_by_name.invalidate (); } } -void DeviceModel::set_cell_index (db::cell_index_type ci) +void DeviceAbstract::set_cell_index (db::cell_index_type ci) { m_cell_index = ci; if (mp_netlist) { - mp_netlist->m_device_model_by_cell_index.invalidate (); + mp_netlist->m_device_abstract_by_cell_index.invalidate (); } } -size_t DeviceModel::cluster_id_for_terminal (size_t terminal_id) const +size_t DeviceAbstract::cluster_id_for_terminal (size_t terminal_id) const { return terminal_id < m_terminal_cluster_ids.size () ? m_terminal_cluster_ids [terminal_id] : 0; } -void DeviceModel::set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id) +void DeviceAbstract::set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id) { if (m_terminal_cluster_ids.size () <= terminal_id) { m_terminal_cluster_ids.resize (terminal_id + 1, 0); diff --git a/src/db/db/dbDeviceAbstract.h b/src/db/db/dbDeviceAbstract.h index ac515befe..54ffba93c 100644 --- a/src/db/db/dbDeviceAbstract.h +++ b/src/db/db/dbDeviceAbstract.h @@ -20,8 +20,8 @@ */ -#ifndef _HDR_dbDeviceModel -#define _HDR_dbDeviceModel +#ifndef _HDR_dbDeviceAbstract +#define _HDR_dbDeviceAbstract #include "dbCommon.h" #include "dbNet.h" @@ -37,39 +37,39 @@ namespace db class Netlist; /** - * @brief A device model + * @brief A device abstract * - * A device model represents the geometrical properties of a device. It basically links + * A device abstract represents the geometrical properties of a device. It basically links * to a cell and clusters for indicating the terminal geometry of the device. */ -class DB_PUBLIC DeviceModel +class DB_PUBLIC DeviceAbstract : public tl::Object { public: /** * @brief Default constructor */ - DeviceModel (); + DeviceAbstract (); /** * @brief The constructor */ - DeviceModel (db::DeviceClass *device_class, const std::string &name = std::string ()); + DeviceAbstract (db::DeviceClass *device_class, const std::string &name = std::string ()); /** * @brief Copy constructor */ - DeviceModel (const DeviceModel &other); + DeviceAbstract (const DeviceAbstract &other); /** * @brief Assignment */ - DeviceModel &operator= (const DeviceModel &other); + DeviceAbstract &operator= (const DeviceAbstract &other); /** * @brief Destructor */ - ~DeviceModel (); + ~DeviceAbstract (); /** * @brief Gets the device class @@ -89,7 +89,7 @@ public: /** * @brief Gets the netlist the device lives in (const version) - * This pointer is 0 if the device model isn't added to a netlist + * This pointer is 0 if the device abstract isn't added to a netlist */ const Netlist *netlist () const { @@ -98,7 +98,7 @@ public: /** * @brief Gets the netlist the device lives in (non-const version) - * This pointer is 0 if the device model isn't added to a netlist + * This pointer is 0 if the device abstract isn't added to a netlist */ Netlist *netlist () { diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index db9bed42b..bd3a0244e 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -354,7 +354,7 @@ db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell std::set device_cells; if (! with_device_cells && mp_netlist.get ()) { - for (db::Netlist::device_model_iterator i = mp_netlist->begin_device_models (); i != mp_netlist->end_device_models (); ++i) { + for (db::Netlist::device_abstract_iterator i = mp_netlist->begin_device_abstracts (); i != mp_netlist->end_device_abstracts (); ++i) { device_cells.insert (i->cell_index ()); } } @@ -448,7 +448,7 @@ static bool deliver_shapes_of_net_nonrecursive (const db::Netlist *nl, const db: for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ) { db::cell_index_type cci = rci.cell_index (); - if (cci != prev_ci && cci != ci && (! nl || nl->circuit_by_cell_index (cci) || nl->device_model_by_cell_index (cci))) { + if (cci != prev_ci && cci != ci && (! nl || nl->circuit_by_cell_index (cci) || nl->device_abstract_by_cell_index (cci))) { rci.skip_cell (); @@ -563,7 +563,7 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & if (cm == cmap.end ()) { const char *name_prefix = 0; - if (mp_netlist->device_model_by_cell_index (subci)) { + if (mp_netlist->device_abstract_by_cell_index (subci)) { name_prefix = device_cell_name_prefix; } else { name_prefix = circuit_cell_name_prefix; diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 4030a5a00..4379fb202 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -398,7 +398,7 @@ public: * Recursive mode is picked when a cell name prefix is given. The new cells will be * named like cell_name_prefix + circuit name. * - * If a device cell name prefix is given, cells will be produced for each device model + * If a device cell name prefix is given, cells will be produced for each device abstract * using a name like device_cell_name_prefix + device name. * * @param target The target layout @@ -427,7 +427,7 @@ public: * * Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built * to accomodate the subnets (see build_net in recursive mode). * - * If a device cell name prefix is given, cells will be produced for each device model + * If a device cell name prefix is given, cells will be produced for each device abstract * using a name like device_cell_name_prefix + device name. * * @param cmap The mapping of internal layout to target layout for the circuit mapping diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index efa35d487..dd9af4889 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -289,9 +289,9 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) std::string name; read_word_or_quoted (name); - db::DeviceModel *dm = new db::DeviceModel (); + db::DeviceAbstract *dm = new db::DeviceAbstract (); dm->set_name (name); - l2n->netlist ()->add_device_model (dm); + l2n->netlist ()->add_device_abstract (dm); db::cell_index_type ci = l2n->internal_layout ()->add_cell (name.c_str ()); dm->set_cell_index (ci); @@ -441,20 +441,20 @@ LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circui std::string dmname; read_word_or_quoted (dmname); - db::DeviceModel *dm = 0; - for (db::Netlist::device_model_iterator i = l2n->netlist ()->begin_device_models (); i != l2n->netlist ()->end_device_models (); ++i) { + db::DeviceAbstract *dm = 0; + for (db::Netlist::device_abstract_iterator i = l2n->netlist ()->begin_device_abstracts (); i != l2n->netlist ()->end_device_abstracts (); ++i) { if (i->name () == dmname) { dm = i.operator-> (); } } if (! dm) { - throw tl::Exception (tl::to_string (tr ("Not a valid device model name: ")) + dmname); + throw tl::Exception (tl::to_string (tr ("Not a valid device abstract name: ")) + dmname); } db::Device *device = new db::Device (); device->set_device_class (const_cast (dm->device_class ())); - device->set_device_model (dm); + device->set_device_abstract (dm); device->set_name (name); circuit->add_device (device); @@ -656,7 +656,7 @@ LayoutToNetlistStandardReader::read_subcircuit (db::LayoutToNetlist *l2n, db::Ci } void -LayoutToNetlistStandardReader::read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceModel *dm, db::DeviceClass *dc) +LayoutToNetlistStandardReader::read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc) { Brace br (this); diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h index 2c0d7e996..f54fc76ee 100644 --- a/src/db/db/dbLayoutToNetlistReader.h +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -38,7 +38,7 @@ namespace l2n_std_reader { class LayoutToNetlist; class Circuit; class Cell; -class DeviceModel; +class DeviceAbstract; class DeviceClass; class Region; @@ -99,7 +99,7 @@ private: void read_pin (db::LayoutToNetlist *l2n, db::Circuit *circuit); db::CellInstArray read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs); db::CellInstArray read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs); - void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceModel *dm, db::DeviceClass *dc); + void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc); std::pair read_geometry (db::LayoutToNetlist *l2n); }; diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 7d04696ff..1a2092bb2 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -48,7 +48,7 @@ private: void write (const db::LayoutToNetlist *l2n, const db::Net &net); void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit); void write (const db::LayoutToNetlist *l2n, const db::Device &device); - void write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model); + void write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract); void write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname); }; @@ -144,11 +144,11 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) } - if (nl->begin_device_models () != nl->end_device_models () && ! Keys::is_short ()) { + if (nl->begin_device_abstracts () != nl->end_device_abstracts () && ! Keys::is_short ()) { *mp_stream << endl << "# Device abstracts section" << endl; *mp_stream << "# Device abstracts list the pin shapes of the devices." << endl; } - for (db::Netlist::const_device_model_iterator m = nl->begin_device_models (); m != nl->end_device_models (); ++m) { + for (db::Netlist::const_abstract_model_iterator m = nl->begin_device_abstracts (); m != nl->end_device_abstracts (); ++m) { if (m->device_class ()) { *mp_stream << Keys::device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl; write (l2n, *m); @@ -276,7 +276,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net // vanish in "purge" but the clusters will still be there we need to recursive into clusters from // unknown cells. db::cell_index_type ci = si.cell_index (); - if (ci != prev_ci && ci != cci && (l2n->netlist ()->circuit_by_cell_index (ci) || l2n->netlist ()->device_model_by_cell_index (ci))) { + if (ci != prev_ci && ci != cci && (l2n->netlist ()->circuit_by_cell_index (ci) || l2n->netlist ()->device_abstract_by_cell_index (ci))) { si.skip_cell (); @@ -359,9 +359,9 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Sub } template -void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model) +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract) { - const std::vector &td = device_model.device_class ()->terminal_definitions (); + const std::vector &td = device_abstract.device_class ()->terminal_definitions (); const db::hier_clusters &clusters = l2n->net_clusters (); const db::Connectivity &conn = l2n->connectivity (); @@ -372,7 +372,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Dev for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { - const db::local_cluster &lc = clusters.clusters_per_cell (device_model.cell_index ()).cluster_by_id (device_model.cluster_id_for_terminal (t->id ())); + const db::local_cluster &lc = clusters.clusters_per_cell (device_abstract.cell_index ()).cluster_by_id (device_abstract.cluster_id_for_terminal (t->id ())); for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { *mp_stream << indent2; @@ -396,8 +396,8 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Dev *mp_stream << indent1 << Keys::device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ()); - tl_assert (device.device_model () != 0); - *mp_stream << " " << tl::to_word_or_quoted_string (device.device_model ()->name ()) << endl; + tl_assert (device.device_abstract () != 0); + *mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl; *mp_stream << indent2 << Keys::location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl; diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index bbeb53ca0..6cc04ea90 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -34,32 +34,32 @@ Netlist::Netlist () : m_valid_topology (false), m_lock_count (0), m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits), - m_device_model_by_name (this, &Netlist::begin_device_models, &Netlist::end_device_models), - m_device_model_by_cell_index (this, &Netlist::begin_device_models, &Netlist::end_device_models) + m_device_abstract_by_name (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts), + m_device_abstract_by_cell_index (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts) { m_circuits.changed ().add (this, &Netlist::invalidate_topology); m_circuits.changed ().add (this, &Netlist::circuits_changed); - m_device_models.changed ().add (this, &Netlist::device_models_changed); + m_device_abstracts.changed ().add (this, &Netlist::device_abstracts_changed); } Netlist::Netlist (const Netlist &other) : gsi::ObjectBase (other), tl::Object (other), m_valid_topology (false), m_lock_count (0), m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits), - m_device_model_by_name (this, &Netlist::begin_device_models, &Netlist::end_device_models), - m_device_model_by_cell_index (this, &Netlist::begin_device_models, &Netlist::end_device_models) + m_device_abstract_by_name (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts), + m_device_abstract_by_cell_index (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts) { operator= (other); m_circuits.changed ().add (this, &Netlist::invalidate_topology); m_circuits.changed ().add (this, &Netlist::circuits_changed); - m_device_models.changed ().add (this, &Netlist::device_models_changed); + m_device_abstracts.changed ().add (this, &Netlist::device_abstracts_changed); } Netlist::~Netlist () { m_circuits.changed ().remove (this, &Netlist::invalidate_topology); m_circuits.changed ().remove (this, &Netlist::circuits_changed); - m_device_models.changed ().remove (this, &Netlist::device_models_changed); + m_device_abstracts.changed ().remove (this, &Netlist::device_abstracts_changed); } Netlist &Netlist::operator= (const Netlist &other) @@ -75,18 +75,18 @@ Netlist &Netlist::operator= (const Netlist &other) m_device_classes.push_back (dc_new); } - std::map dmt; - for (const_device_model_iterator dm = other.begin_device_models (); dm != other.end_device_models (); ++dm) { - DeviceModel *dm_new = new DeviceModel (*dm); + std::map dmt; + for (const_abstract_model_iterator dm = other.begin_device_abstracts (); dm != other.end_device_abstracts (); ++dm) { + DeviceAbstract *dm_new = new DeviceAbstract (*dm); dmt [dm.operator-> ()] = dm_new; - m_device_models.push_back (dm_new); + m_device_abstracts.push_back (dm_new); } std::map ct; for (const_circuit_iterator i = other.begin_circuits (); i != other.end_circuits (); ++i) { Circuit *ct_new = new Circuit (*i); ct_new->translate_device_classes (dct); - ct_new->translate_device_models (dmt); + ct_new->translate_device_abstracts (dmt); ct [i.operator-> ()] = ct_new; add_circuit (ct_new); } @@ -105,10 +105,10 @@ void Netlist::circuits_changed () m_circuit_by_name.invalidate (); } -void Netlist::device_models_changed () +void Netlist::device_abstracts_changed () { - m_device_model_by_cell_index.invalidate (); - m_device_model_by_name.invalidate (); + m_device_abstract_by_cell_index.invalidate (); + m_device_abstract_by_name.invalidate (); } void Netlist::invalidate_topology () @@ -353,7 +353,7 @@ Netlist::const_bottom_up_circuit_iterator Netlist::end_bottom_up () const void Netlist::clear () { m_device_classes.clear (); - m_device_models.clear (); + m_device_abstracts.clear (); m_circuits.clear (); } @@ -381,16 +381,16 @@ void Netlist::remove_device_class (DeviceClass *device_class) m_device_classes.erase (device_class); } -void Netlist::add_device_model (DeviceModel *device_model) +void Netlist::add_device_abstract (DeviceAbstract *device_abstract) { - m_device_models.push_back (device_model); - device_model->set_netlist (this); + m_device_abstracts.push_back (device_abstract); + device_abstract->set_netlist (this); } -void Netlist::remove_device_model (DeviceModel *device_model) +void Netlist::remove_device_abstract (DeviceAbstract *device_abstract) { - device_model->set_netlist (0); - m_device_models.erase (device_model); + device_abstract->set_netlist (0); + m_device_abstracts.erase (device_abstract); } void Netlist::purge_nets () diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index cbb822a36..86bfd9298 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -26,7 +26,7 @@ #include "dbCommon.h" #include "dbCircuit.h" #include "dbDeviceClass.h" -#include "dbDeviceModel.h" +#include "dbDeviceAbstract.h" #include "tlVector.h" @@ -52,9 +52,9 @@ public: typedef tl::shared_collection device_class_list; typedef device_class_list::const_iterator const_device_class_iterator; typedef device_class_list::iterator device_class_iterator; - typedef tl::shared_collection device_model_list; - typedef device_model_list::const_iterator const_device_model_iterator; - typedef device_model_list::iterator device_model_iterator; + typedef tl::shared_collection device_abstract_list; + typedef device_abstract_list::const_iterator const_abstract_model_iterator; + typedef device_abstract_list::iterator device_abstract_iterator; typedef tl::vector::const_iterator top_down_circuit_iterator; typedef tl::vector::const_iterator const_top_down_circuit_iterator; typedef tl::vector::const_reverse_iterator bottom_up_circuit_iterator; @@ -306,87 +306,87 @@ public: } /** - * @brief Adds a device model to this netlist + * @brief Adds a device abstract to this netlist * * The netlist takes over ownership of the object. */ - void add_device_model (DeviceModel *device_model); + void add_device_abstract (DeviceAbstract *device_abstract); /** - * @brief Deletes a device model from the netlist + * @brief Deletes a device abstract from the netlist */ - void remove_device_model (DeviceModel *device_model); + void remove_device_abstract (DeviceAbstract *device_abstract); /** - * @brief Begin iterator for the device models of the netlist (non-const version) + * @brief Begin iterator for the device abstracts of the netlist (non-const version) */ - device_model_iterator begin_device_models () + device_abstract_iterator begin_device_abstracts () { - return m_device_models.begin (); + return m_device_abstracts.begin (); } /** - * @brief End iterator for the device models of the netlist (non-const version) + * @brief End iterator for the device abstracts of the netlist (non-const version) */ - device_model_iterator end_device_models () + device_abstract_iterator end_device_abstracts () { - return m_device_models.end (); + return m_device_abstracts.end (); } /** - * @brief Begin iterator for the device models of the netlist (const version) + * @brief Begin iterator for the device abstracts of the netlist (const version) */ - const_device_model_iterator begin_device_models () const + const_abstract_model_iterator begin_device_abstracts () const { - return m_device_models.begin (); + return m_device_abstracts.begin (); } /** - * @brief End iterator for the device models of the netlist (const version) + * @brief End iterator for the device abstracts of the netlist (const version) */ - const_device_model_iterator end_device_models () const + const_abstract_model_iterator end_device_abstracts () const { - return m_device_models.end (); + return m_device_abstracts.end (); } /** - * @brief Gets the device model with the given name + * @brief Gets the device abstract with the given name * - * If no device model with that name exists, null is returned. + * If no device abstract with that name exists, null is returned. */ - DeviceModel *device_model_by_name (const std::string &name) + DeviceAbstract *device_abstract_by_name (const std::string &name) { - return m_device_model_by_name.object_by (name); + return m_device_abstract_by_name.object_by (name); } /** - * @brief Gets the device model with the given name (const version) + * @brief Gets the device abstract with the given name (const version) * - * If no device model with that name exists, null is returned. + * If no device abstract with that name exists, null is returned. */ - const DeviceModel *device_model_by_name (const std::string &name) const + const DeviceAbstract *device_abstract_by_name (const std::string &name) const { - return m_device_model_by_name.object_by (name); + return m_device_abstract_by_name.object_by (name); } /** - * @brief Gets the device model with the given cell index + * @brief Gets the device abstract with the given cell index * - * If no device model with that cell index exists, null is returned. + * If no device abstract with that cell index exists, null is returned. */ - DeviceModel *device_model_by_cell_index (db::cell_index_type cell_index) + DeviceAbstract *device_abstract_by_cell_index (db::cell_index_type cell_index) { - return m_device_model_by_cell_index.object_by (cell_index); + return m_device_abstract_by_cell_index.object_by (cell_index); } /** - * @brief Gets the device model with the given cell index (const version) + * @brief Gets the device abstract with the given cell index (const version) * - * If no device model with that cell index exists, null is returned. + * If no device abstract with that cell index exists, null is returned. */ - const DeviceModel *device_model_by_cell_index (db::cell_index_type cell_index) const + const DeviceAbstract *device_abstract_by_cell_index (db::cell_index_type cell_index) const { - return m_device_model_by_cell_index.object_by (cell_index); + return m_device_abstract_by_cell_index.object_by (cell_index); } /** @@ -423,11 +423,11 @@ public: private: friend class Circuit; - friend class DeviceModel; + friend class DeviceAbstract; circuit_list m_circuits; device_class_list m_device_classes; - device_model_list m_device_models; + device_abstract_list m_device_abstracts; bool m_valid_topology; int m_lock_count; tl::vector m_top_down_circuits; @@ -436,13 +436,13 @@ private: size_t m_top_circuits; object_by_attr > m_circuit_by_name; object_by_attr > m_circuit_by_cell_index; - object_by_attr > m_device_model_by_name; - object_by_attr > m_device_model_by_cell_index; + object_by_attr > m_device_abstract_by_name; + object_by_attr > m_device_abstract_by_cell_index; void invalidate_topology (); void validate_topology (); void circuits_changed (); - void device_models_changed (); + void device_abstracts_changed (); const tl::vector &child_circuits (Circuit *circuit); const tl::vector &parent_circuits (Circuit *circuit); diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 5749bcb86..821944950 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -169,7 +169,7 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: for (std::set::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) { // skip device cells from previous extractions - if (m_netlist->device_model_by_cell_index (*ci)) { + if (m_netlist->device_abstract_by_cell_index (*ci)) { continue; } @@ -254,14 +254,14 @@ void NetlistDeviceExtractor::push_new_devices () db::PropertiesRepository::properties_set ps; - std::map >::iterator c = m_device_cells.find (key); + std::map >::iterator c = m_device_cells.find (key); if (c == m_device_cells.end ()) { std::string cell_name = "D$" + mp_device_class->name (); db::Cell &device_cell = mp_layout->cell (mp_layout->add_cell (cell_name.c_str ())); - db::DeviceModel *dm = new db::DeviceModel (mp_device_class, mp_layout->cell_name (device_cell.cell_index ())); - m_netlist->add_device_model (dm); + db::DeviceAbstract *dm = new db::DeviceAbstract (mp_device_class, mp_layout->cell_name (device_cell.cell_index ())); + m_netlist->add_device_abstract (dm); dm->set_cell_index (device_cell.cell_index ()); c = m_device_cells.insert (std::make_pair (key, std::make_pair (device_cell.cell_index (), dm))).first; @@ -295,7 +295,7 @@ void NetlistDeviceExtractor::push_new_devices () } // make the cell index known to the device - device->set_device_model (c->second.second); + device->set_device_abstract (c->second.second); // Build a property set for the device ID ps.clear (); diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index 8c6734c63..0be8e5f7f 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -508,7 +508,7 @@ private: std::vector m_layers; error_list m_errors; std::map m_new_devices; - std::map > m_device_cells; + std::map > m_device_cells; // no copying NetlistDeviceExtractor (const NetlistDeviceExtractor &); diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 5bf37763e..b1227f916 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -71,10 +71,10 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect continue; } - db::DeviceModel *dm = nl.device_model_by_cell_index (*cid); + db::DeviceAbstract *dm = nl.device_abstract_by_cell_index (*cid); if (dm) { - // make the terminal to cluster ID connections for the device model from the device cells - make_device_model_connections (dm, clusters); + // make the terminal to cluster ID connections for the device abstract from the device cells + make_device_abstract_connections (dm, clusters); continue; } @@ -129,9 +129,9 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } void -NetlistExtractor::make_device_model_connections (db::DeviceModel *dm, const connected_clusters_type &clusters) +NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters) { - // make the terminal to cluster ID connections for the device model from the device cells + // make the terminal to cluster ID connections for the device abstract from the device cells if (m_terminal_annot_name_id.first) { diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index c896d349d..3189eefec 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -37,7 +37,7 @@ class Circuit; class SubCircuit; class Net; class Device; -class DeviceModel; +class DeviceAbstract; /** * @brief The Netlist Extractor @@ -154,9 +154,9 @@ private: db::Net *net); /** - * @brief Makes the terminal to cluster ID connections of the device model + * @brief Makes the terminal to cluster ID connections of the device abstract */ - void make_device_model_connections (db::DeviceModel *dm, const connected_clusters_type &clusters); + void make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters); }; diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 1009f26fd..a44ae71d2 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -136,27 +136,27 @@ Class decl_dbDevice ("db", "Device", #if 0 // TODO: activate once the geometry API is opened (clusters at al.) -Class decl_dbDeviceModel ("db", "DeviceModel", - gsi::method ("netlist", (db::Netlist *(db::DeviceModel::*) ()) &db::DeviceModel::netlist, - "@brief Gets the netlist the device model lives in." +Class decl_dbDeviceAbstract ("db", "DeviceAbstract", + gsi::method ("netlist", (db::Netlist *(db::DeviceAbstract::*) ()) &db::DeviceAbstract::netlist, + "@brief Gets the netlist the device abstract lives in." ) + - gsi::method ("name=", &db::DeviceModel::set_name, gsi::arg ("name"), - "@brief Sets the name of the device model.\n" - "Device names are used to name a device model inside a netlist file. " + gsi::method ("name=", &db::DeviceAbstract::set_name, gsi::arg ("name"), + "@brief Sets the name of the device abstract.\n" + "Device names are used to name a device abstract inside a netlist file. " "Device names should be unique within a netlist." ) + - gsi::method ("name", &db::DeviceModel::name, - "@brief Gets the name of the device model.\n" + gsi::method ("name", &db::DeviceAbstract::name, + "@brief Gets the name of the device abstract.\n" ) + - gsi::method ("cell_index", &db::DeviceModel::cell_index, - "@brief Gets the cell index of the device model.\n" + gsi::method ("cell_index", &db::DeviceAbstract::cell_index, + "@brief Gets the cell index of the device abstract.\n" "This is the cell that represents the device." ) + - gsi::method ("cluster_id_for_terminal", &db::DeviceModel::cluster_id_for_terminal, gsi::arg ("terminal_id"), + gsi::method ("cluster_id_for_terminal", &db::DeviceAbstract::cluster_id_for_terminal, gsi::arg ("terminal_id"), "@brief Gets the cluster ID for the given terminal.\n" "The cluster ID links the terminal to geometrical shapes within the clusters of the cell (see \\cell_index)" ), - "@brief A geometrical device model\n" + "@brief A geometrical device abstract\n" "This class represents the geometrical model for the device. It links into the extracted layout " "to a cell which holds the terminal shapes for the device.\n" "\n" diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 4eab0b589..1ac2bbdf9 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -93,7 +93,7 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters< for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { - db::cell_index_type dci = d->device_model ()->cell_index (); + db::cell_index_type dci = d->device_abstract ()->cell_index (); if (device_cells_seen.find (dci) != device_cells_seen.end ()) { continue; @@ -114,7 +114,7 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters< const std::vector &td = d->device_class ()->terminal_definitions (); for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { - const db::local_cluster &dc = clusters.clusters_per_cell (dci).cluster_by_id (d->device_model ()->cluster_id_for_terminal (t->id ())); + const db::local_cluster &dc = clusters.clusters_per_cell (dci).cluster_by_id (d->device_abstract ()->cluster_id_for_terminal (t->id ())); for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { db::Shapes &target = device_cell.shapes (m->second); diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 4af8413b8..725573803 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -125,8 +125,8 @@ static std::string netlist2 (const db::Circuit &c) pins += net ? net->name () : std::string ("(null)"); } res += " D" + d->name (); - if (d->device_model ()) { - res += "/" + d->device_model ()->name (); + if (d->device_abstract ()) { + res += "/" + d->device_abstract ()->name (); } res += ":" + pins + "\n"; } @@ -501,13 +501,13 @@ TEST(4_NetlistSubcircuits) dc->add_terminal_definition (db::DeviceTerminalDefinition ("B", "")); nl->add_device_class (dc); - db::DeviceModel *dm = new db::DeviceModel (); + db::DeviceAbstract *dm = new db::DeviceAbstract (); dm->set_device_class (dc); EXPECT_EQ (dm->device_class () == dc, true); dm->set_name ("dm2"); dm->set_cell_index (42); dm->set_cluster_id_for_terminal (0, 17); - nl->add_device_model (dm); + nl->add_device_abstract (dm); db::Circuit *c1 = new db::Circuit (); c1->set_cell_index (17); @@ -540,7 +540,7 @@ TEST(4_NetlistSubcircuits) db::Device *d = new db::Device (dc, dm, "D"); c2->add_device (d); - EXPECT_EQ (d->device_model ()->name (), "dm2"); + EXPECT_EQ (d->device_abstract ()->name (), "dm2"); EXPECT_EQ (refs2string (c2), ""); db::SubCircuit *sc1 = new db::SubCircuit (c2); @@ -1042,24 +1042,24 @@ TEST(12_NetlistTopology) EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1"); } -TEST(13_DeviceModel) +TEST(13_DeviceAbstract) { db::Netlist nl; - db::DeviceModel *dm = new db::DeviceModel (0, "name"); - nl.add_device_model (dm); + db::DeviceAbstract *dm = new db::DeviceAbstract (0, "name"); + nl.add_device_abstract (dm); EXPECT_EQ (dm->netlist () == &nl, true); EXPECT_EQ (dm->device_class () == 0, true); EXPECT_EQ (dm->name (), "name"); - EXPECT_EQ (nl.device_model_by_name ("name") == dm, true); - EXPECT_EQ (nl.device_model_by_name ("name2") == 0, true); - EXPECT_EQ (nl.device_model_by_name ("does_not_exist") == 0, true); + EXPECT_EQ (nl.device_abstract_by_name ("name") == dm, true); + EXPECT_EQ (nl.device_abstract_by_name ("name2") == 0, true); + EXPECT_EQ (nl.device_abstract_by_name ("does_not_exist") == 0, true); dm->set_name ("name2"); EXPECT_EQ (dm->name (), "name2"); - EXPECT_EQ (nl.device_model_by_name ("name") == 0, true); - EXPECT_EQ (nl.device_model_by_name ("name2") == dm, true); - EXPECT_EQ (nl.device_model_by_name ("does_not_exist") == 0, true); + EXPECT_EQ (nl.device_abstract_by_name ("name") == 0, true); + EXPECT_EQ (nl.device_abstract_by_name ("name2") == dm, true); + EXPECT_EQ (nl.device_abstract_by_name ("does_not_exist") == 0, true); dm->set_cluster_id_for_terminal (1, 17); dm->set_cluster_id_for_terminal (0, 42); @@ -1067,14 +1067,14 @@ TEST(13_DeviceModel) EXPECT_EQ (dm->cluster_id_for_terminal (1), size_t (17)); dm->set_cell_index (5); - EXPECT_EQ (nl.device_model_by_cell_index (5) == dm, true); - EXPECT_EQ (nl.device_model_by_cell_index (17) == 0, true); + EXPECT_EQ (nl.device_abstract_by_cell_index (5) == dm, true); + EXPECT_EQ (nl.device_abstract_by_cell_index (17) == 0, true); EXPECT_EQ (dm->cell_index (), db::cell_index_type (5)); - EXPECT_EQ (nl.begin_device_models () == nl.end_device_models (), false); - EXPECT_EQ (nl.begin_device_models ()->name (), "name2"); + EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), false); + EXPECT_EQ (nl.begin_device_abstracts ()->name (), "name2"); - nl.remove_device_model (dm); + nl.remove_device_abstract (dm); - EXPECT_EQ (nl.begin_device_models () == nl.end_device_models (), true); + EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), true); } From 68fe668567764d3466ac49042f9f117010aed943 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 22 Jan 2019 07:42:44 +0100 Subject: [PATCH 207/335] WIP: performance improvement. --- src/db/db/dbDeepShapeStore.cc | 2 +- src/db/db/dbNetlistDeviceExtractor.cc | 116 +++++++++-- src/db/db/dbNetlistDeviceExtractor.h | 3 +- src/db/unit_tests/dbNetlistExtractorTests.cc | 207 +++++++++++++++++++ 4 files changed, 311 insertions(+), 17 deletions(-) diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index b7bf576d3..cf7c48bcf 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -433,7 +433,7 @@ DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_ } if (! skip) { - cm->second.map (m->first.first, m->second); + cm->second.map (m->second, m->first.first); } } diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 821944950..e45cedf92 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -25,6 +25,9 @@ #include "dbHierNetworkProcessor.h" #include "dbDeepRegion.h" +#include "tlProgress.h" +#include "tlTimer.h" + namespace db { @@ -156,23 +159,53 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: } // collect the cells below the top cell + std::set all_called_cells; + all_called_cells.insert (cell.cell_index ()); + cell.collect_called_cells (all_called_cells); + + // ignore device cells from previous extractions std::set called_cells; - called_cells.insert (cell.cell_index ()); - cell.collect_called_cells (called_cells); + for (std::set::const_iterator ci = all_called_cells.begin (); ci != all_called_cells.end (); ++ci) { + if (! m_netlist->device_abstract_by_cell_index (*ci)) { + called_cells.insert (*ci); + } + } + all_called_cells.clear (); // build the device clusters db::Connectivity device_conn = get_connectivity (layout, layers); db::hier_clusters device_clusters; device_clusters.build (layout, cell, shape_iter_flags, device_conn); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Extracting devices"))); + + // count effort and make a progress reporter + + size_t n = 0; + for (std::set::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) { + db::connected_clusters cc = device_clusters.clusters_per_cell (*ci); + for (db::connected_clusters::all_iterator c = cc.begin_all (); !c.at_end(); ++c) { + if (cc.is_root (*c)) { + ++n; + } + } + } + + tl::RelativeProgress progress (tl::to_string (tr ("Extracting devices")), n, 1); + + struct ExtractorCacheValueType { + ExtractorCacheValueType () : circuit (0) { } + db::Circuit *circuit; + db::Vector disp; + std::map geometry; + }; + + typedef std::map, ExtractorCacheValueType> extractor_cache_type; + extractor_cache_type extractor_cache; + // for each cell investigate the clusters for (std::set::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) { - // skip device cells from previous extractions - if (m_netlist->device_abstract_by_cell_index (*ci)) { - continue; - } - m_cell_index = *ci; std::map::const_iterator c2c = circuits_by_cell.find (*ci); @@ -200,6 +233,8 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: continue; } + ++progress; + // build layer geometry from the cluster found std::vector layer_geometry; @@ -210,28 +245,55 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: for (db::recursive_cluster_shape_iterator si (device_clusters, *l, *ci, *c); ! si.at_end(); ++si) { insert_into_region (*si, si.trans (), r); } + // r.merge ()??? } - // do the actual device extraction - extract_devices (layer_geometry); + db::Box box; + for (std::vector::const_iterator g = layer_geometry.begin (); g != layer_geometry.end (); ++g) { + box += g->bbox (); + } - // push the new devices to the layout - push_new_devices (); + db::Vector disp = box.p1 () - db::Point (); + for (std::vector::iterator g = layer_geometry.begin (); g != layer_geometry.end (); ++g) { + g->transform (db::Disp (-disp)); + } + + extractor_cache_type::const_iterator ec = extractor_cache.find (layer_geometry); + if (ec == extractor_cache.end ()) { + + // do the actual device extraction + extract_devices (layer_geometry); + + // push the new devices to the layout + push_new_devices (disp); + + ExtractorCacheValueType &ecv = extractor_cache [layer_geometry]; + ecv.disp = disp; + ecv.circuit = mp_circuit; + ecv.geometry.swap (m_new_devices); + + } else { + + push_cached_devices (ec->second.circuit, ec->second.geometry, ec->second.disp, disp); + + } } } } -void NetlistDeviceExtractor::push_new_devices () +void NetlistDeviceExtractor::push_new_devices (const db::Vector &disp_cache) { - db::VCplxTrans dbu_inv = db::CplxTrans (mp_layout->dbu ()).inverted (); + db::CplxTrans dbu = db::CplxTrans (mp_layout->dbu ()); + db::VCplxTrans dbu_inv = dbu.inverted (); for (std::map::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { db::Device *device = mp_circuit->device_by_id (d->first); db::Vector disp = dbu_inv * device->position () - db::Point (); + device->set_position (device->position () + dbu * disp_cache); DeviceCellKey key; @@ -302,12 +364,36 @@ void NetlistDeviceExtractor::push_new_devices () ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (d->first))); db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); - db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (c->second.first), db::Trans (disp)), pi); + db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (c->second.first), db::Trans (disp_cache + disp)), pi); mp_layout->cell (m_cell_index).insert (inst); } +} - m_new_devices.clear (); +void NetlistDeviceExtractor::push_cached_devices (db::Circuit *circuit, const std::map &cached_devices, const db::Vector &disp_cache, const db::Vector &new_disp) +{ + db::CplxTrans dbu = db::CplxTrans (mp_layout->dbu ()); + db::VCplxTrans dbu_inv = dbu.inverted (); + db::PropertiesRepository::properties_set ps; + + for (std::map::const_iterator d = cached_devices.begin (); d != cached_devices.end (); ++d) { + + db::Device *cached_device = circuit->device_by_id (d->first); + db::Vector disp = dbu_inv * cached_device->position () - disp_cache - db::Point (); + + db::Device *device = new db::Device (*cached_device); + mp_circuit->add_device (device); + device->set_position (device->position () + dbu * (new_disp - disp_cache)); + + // Build a property set for the device ID + ps.clear (); + ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (device->id ()))); + db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); + + db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (device->device_abstract ()->cell_index ()), db::Trans (new_disp + disp)), pi); + mp_layout->cell (m_cell_index).insert (inst); + + } } void NetlistDeviceExtractor::setup () diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index 0be8e5f7f..e2b97280d 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -521,7 +521,8 @@ private: void initialize (db::Netlist *nl); void extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector &layers); - void push_new_devices (); + void push_new_devices (const Vector &disp_cache); + void push_cached_devices (db::Circuit *circuit, const std::map &cached_devices, const db::Vector &disp_cache, const db::Vector &new_disp); }; } diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 1ac2bbdf9..add58495f 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -340,3 +340,210 @@ TEST(1_DeviceAndNetExtraction) db::compare_layouts (_this, ly, au); } + +TEST(2_DeviceAndNetExtractionFlat) +{ + db::Layout ly (true); + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + ly.flatten (ly.cell (*ly.begin_top_down ()), -1, true); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("LABEL")); + + // original layers + db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); + db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss); + db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss); + db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss); + db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss); + db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss); + db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss); + db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss); + db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss); + db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss); + + // derived regions + + db::Region rpactive = ractive & rnwell; + db::Region rpgate = rpactive & rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = ractive - rnwell; + db::Region rngate = rnactive & rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + + // perform the extraction + + db::Netlist nl; + db::hier_clusters cl; + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + pmos_ex.extract (dss, dl, nl, cl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + nmos_ex.extract (dss, dl, nl, cl); + + // perform the net extraction + + db::NetlistExtractor net_ex; + + db::Connectivity conn; + // Intra-layer + conn.connect (rpsd); + conn.connect (rnsd); + conn.connect (rpoly); + conn.connect (rdiff_cont); + conn.connect (rpoly_cont); + conn.connect (rmetal1); + conn.connect (rvia1); + conn.connect (rmetal2); + // Inter-layer + conn.connect (rpsd, rdiff_cont); + conn.connect (rnsd, rdiff_cont); + conn.connect (rpoly, rpoly_cont); + conn.connect (rpoly_cont, rmetal1); + conn.connect (rdiff_cont, rmetal1); + conn.connect (rmetal1, rvia1); + conn.connect (rvia1, rmetal2); + conn.connect (rpoly, rpoly_lbl); // attaches labels + conn.connect (rmetal1, rmetal1_lbl); // attaches labels + conn.connect (rmetal2, rmetal2_lbl); // attaches labels + + // extract the nets + + net_ex.extract_nets (dss, conn, nl, cl); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + std::map dump_map; + dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); + dump_nets_to_layout (nl, cl, ly, dump_map, cm); + + // compare netlist as string + // NOTE: some of the nets are called IN,OUT but are different ones. They + // happen to be the same because they share the same label. + EXPECT_EQ (nl.to_string (), + "Circuit RINGO ():\n" + " DPMOS $1 (S='IN,FB',G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=VDD,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $3 (S=$18,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $4 (S=VDD,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $5 (S=$16,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $6 (S=VDD,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $7 (S=$14,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $8 (S=VDD,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $9 (S=$12,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $10 (S=VDD,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $11 (S=$4,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $12 (S=VDD,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $13 (S=$8,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $14 (S=VDD,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $15 (S=$6,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $16 (S=VDD,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $17 (S=$2,G='IN,FB',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $18 (S=VDD,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $19 (S=$10,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $20 (S=VDD,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $23 (S=$16,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $24 (S=VSS,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $25 (S=$14,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $26 (S=VSS,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $27 (S=$12,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $28 (S=VSS,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $29 (S=$4,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $30 (S=VSS,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $31 (S=$18,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $32 (S=VSS,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $33 (S=$2,G='IN,FB',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $34 (S=VSS,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $35 (S=$10,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $36 (S=VSS,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $37 (S=$6,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $38 (S=VSS,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $39 (S=$8,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $40 (S=VSS,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_flat.gds"); + + db::compare_layouts (_this, ly, au); +} From fba5bed2a3a6bb03d2ae3629d21fcaaa91989f37 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 23 Jan 2019 01:02:22 +0100 Subject: [PATCH 208/335] Performance improvement of device extractor (taking out a O(N**2) loop) --- src/db/db/dbNetlistDeviceExtractor.cc | 34 +++++++++++++++------------ src/db/db/dbNetlistDeviceExtractor.h | 4 ++-- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index e45cedf92..07103f2a1 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -194,10 +194,9 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: tl::RelativeProgress progress (tl::to_string (tr ("Extracting devices")), n, 1); struct ExtractorCacheValueType { - ExtractorCacheValueType () : circuit (0) { } - db::Circuit *circuit; + ExtractorCacheValueType () { } db::Vector disp; - std::map geometry; + tl::vector devices; }; typedef std::map, ExtractorCacheValueType> extractor_cache_type; @@ -245,7 +244,6 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: for (db::recursive_cluster_shape_iterator si (device_clusters, *l, *ci, *c); ! si.at_end(); ++si) { insert_into_region (*si, si.trans (), r); } - // r.merge ()??? } db::Box box; @@ -269,12 +267,16 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: ExtractorCacheValueType &ecv = extractor_cache [layer_geometry]; ecv.disp = disp; - ecv.circuit = mp_circuit; - ecv.geometry.swap (m_new_devices); + + for (std::map >::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { + ecv.devices.push_back (d->second.first); + } + + m_new_devices.clear (); } else { - push_cached_devices (ec->second.circuit, ec->second.geometry, ec->second.disp, disp); + push_cached_devices (ec->second.devices, ec->second.disp, disp); } @@ -288,16 +290,16 @@ void NetlistDeviceExtractor::push_new_devices (const db::Vector &disp_cache) db::CplxTrans dbu = db::CplxTrans (mp_layout->dbu ()); db::VCplxTrans dbu_inv = dbu.inverted (); - for (std::map::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { + for (std::map >::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { - db::Device *device = mp_circuit->device_by_id (d->first); + db::Device *device = d->second.first; db::Vector disp = dbu_inv * device->position () - db::Point (); device->set_position (device->position () + dbu * disp_cache); DeviceCellKey key; - for (geometry_per_terminal_type::const_iterator t = d->second.begin (); t != d->second.end (); ++t) { + for (geometry_per_terminal_type::const_iterator t = d->second.second.begin (); t != d->second.second.end (); ++t) { std::map > > = key.geometry [t->first]; for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) { std::set &gl = gt [l->first]; @@ -333,7 +335,7 @@ void NetlistDeviceExtractor::push_new_devices (const db::Vector &disp_cache) ps.insert (std::make_pair (m_device_class_propname_id, tl::Variant (mp_device_class->name ()))); device_cell.prop_id (mp_layout->properties_repository ().properties_id (ps)); - for (geometry_per_terminal_type::const_iterator t = d->second.begin (); t != d->second.end (); ++t) { + for (geometry_per_terminal_type::const_iterator t = d->second.second.begin (); t != d->second.second.end (); ++t) { // Build a property set for the device terminal ID ps.clear (); @@ -370,15 +372,15 @@ void NetlistDeviceExtractor::push_new_devices (const db::Vector &disp_cache) } } -void NetlistDeviceExtractor::push_cached_devices (db::Circuit *circuit, const std::map &cached_devices, const db::Vector &disp_cache, const db::Vector &new_disp) +void NetlistDeviceExtractor::push_cached_devices (const tl::vector &cached_devices, const db::Vector &disp_cache, const db::Vector &new_disp) { db::CplxTrans dbu = db::CplxTrans (mp_layout->dbu ()); db::VCplxTrans dbu_inv = dbu.inverted (); db::PropertiesRepository::properties_set ps; - for (std::map::const_iterator d = cached_devices.begin (); d != cached_devices.end (); ++d) { + for (std::vector::const_iterator d = cached_devices.begin (); d != cached_devices.end (); ++d) { - db::Device *cached_device = circuit->device_by_id (d->first); + db::Device *cached_device = *d; db::Vector disp = dbu_inv * cached_device->position () - disp_cache - db::Point (); db::Device *device = new db::Device (*cached_device); @@ -453,7 +455,9 @@ void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id unsigned int layer_index = m_layers [geometry_index]; db::PolygonRef pr (polygon, mp_layout->shape_repository ()); - m_new_devices[device->id ()][terminal_id][layer_index].push_back (pr); + std::pair &dd = m_new_devices[device->id ()]; + dd.first = device; + dd.second[terminal_id][layer_index].push_back (pr); } void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Box &box) diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index e2b97280d..cbd8ec85a 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -507,7 +507,7 @@ private: layer_definitions m_layer_definitions; std::vector m_layers; error_list m_errors; - std::map m_new_devices; + std::map > m_new_devices; std::map > m_device_cells; // no copying @@ -522,7 +522,7 @@ private: void extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector &layers); void push_new_devices (const Vector &disp_cache); - void push_cached_devices (db::Circuit *circuit, const std::map &cached_devices, const db::Vector &disp_cache, const db::Vector &new_disp); + void push_cached_devices (const tl::vector &cached_devices, const db::Vector &disp_cache, const db::Vector &new_disp); }; } From 1cfa3251ced3fa3edc0dd55bd44284b7574d32cf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 23 Jan 2019 22:19:28 +0100 Subject: [PATCH 209/335] Better reproducibility of results in hier processor: hash function of shape ref takes object hash, not pointer hash --- src/db/db/dbHierProcessor.h | 35 ++++++++++-- src/db/db/dbNetlistDeviceExtractor.cc | 1 + src/db/unit_tests/dbNetlistExtractorTests.cc | 56 ++++++++++---------- 3 files changed, 60 insertions(+), 32 deletions(-) diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index 866137bbc..5bf25384b 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -51,12 +51,39 @@ namespace std } }; - template - struct hash > + template + struct hash > { - size_t operator() (const db::polygon_ref &o) const + size_t operator() (const db::shape_ref &o) const { - return hfunc (size_t (o.ptr ()), std::hash () (o.trans ())); + return hfunc (std::hash () (*o.ptr ()), std::hash () (o.trans ())); + } + }; + + template + struct hash > + { + size_t operator() (const db::polygon_ref &o) const + { + return std::hash > () (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::path_ref &o) const + { + return std::hash > () (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::text_ref &o) const + { + return std::hash > () (o); } }; diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 07103f2a1..a0d40cb31 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -173,6 +173,7 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: all_called_cells.clear (); // build the device clusters + db::Connectivity device_conn = get_connectivity (layout, layers); db::hier_clusters device_clusters; device_clusters.build (layout, cell, shape_iter_flags, device_conn); diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index add58495f..22d590b75 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -496,46 +496,46 @@ TEST(2_DeviceAndNetExtractionFlat) // happen to be the same because they share the same label. EXPECT_EQ (nl.to_string (), "Circuit RINGO ():\n" - " DPMOS $1 (S='IN,FB',G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=VDD,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $3 (S=$18,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $4 (S=VDD,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $5 (S=$16,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $6 (S=VDD,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $7 (S=$14,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $8 (S=VDD,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $9 (S=$12,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $10 (S=VDD,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $11 (S=$4,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $12 (S=VDD,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $13 (S=$8,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $14 (S=VDD,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$16,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=VDD,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $3 (S=$14,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $4 (S=VDD,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $5 (S=$12,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $6 (S=VDD,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $9 (S=$4,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $10 (S=VDD,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $11 (S=$8,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $12 (S=VDD,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $13 (S=$2,G='IN,FB',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $14 (S=VDD,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " DPMOS $15 (S=$6,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" " DPMOS $16 (S=VDD,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $17 (S=$2,G='IN,FB',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $18 (S=VDD,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $17 (S=$18,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $18 (S=VDD,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " DPMOS $19 (S=$10,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" " DPMOS $20 (S=VDD,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " DNMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" " DNMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $23 (S=$16,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $24 (S=VSS,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $23 (S=$18,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $24 (S=VSS,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " DNMOS $25 (S=$14,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" " DNMOS $26 (S=VSS,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " DNMOS $27 (S=$12,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" " DNMOS $28 (S=VSS,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" " DNMOS $29 (S=$4,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" " DNMOS $30 (S=VSS,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $31 (S=$18,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $32 (S=VSS,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $33 (S=$2,G='IN,FB',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $34 (S=VSS,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $35 (S=$10,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $36 (S=VSS,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $37 (S=$6,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $38 (S=VSS,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $39 (S=$8,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $40 (S=VSS,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $31 (S=$2,G='IN,FB',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $32 (S=VSS,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $33 (S=$8,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $34 (S=VSS,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $35 (S=$6,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $36 (S=VSS,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $37 (S=$16,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $38 (S=VSS,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $39 (S=$10,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $40 (S=VSS,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" ); // compare the collected test data From a63543589970e7718701fe87013709a34552592b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 23 Jan 2019 23:39:43 +0100 Subject: [PATCH 210/335] Moved some code --- src/db/db/dbHash.h | 76 ++++++++++++++++++++++++++++++++++++ src/db/db/dbHierProcessor.cc | 14 +++---- src/db/db/dbHierProcessor.h | 63 ------------------------------ 3 files changed, 83 insertions(+), 70 deletions(-) diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index ef46366d3..5f5ecb481 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -238,6 +238,18 @@ namespace std } }; + /** + * @brief A hash function for a displacement transformation + */ + template + struct hash > + { + size_t operator() (const db::disp_trans &t) const + { + return hfunc (t.disp ()); + } + }; + /** * @brief Hash value for a complex transformation */ @@ -295,6 +307,54 @@ namespace std } }; + /** + * @brief A hash function for a shape reference + */ + template + struct hash > + { + size_t operator() (const db::shape_ref &o) const + { + return hfunc (std::hash () (*o.ptr ()), std::hash () (o.trans ())); + } + }; + + /** + * @brief A hash function for a polygon reference + */ + template + struct hash > + { + size_t operator() (const db::polygon_ref &o) const + { + return std::hash > () (o); + } + }; + + /** + * @brief A hash function for a path reference + */ + template + struct hash > + { + size_t operator() (const db::path_ref &o) const + { + return std::hash > () (o); + } + }; + + /** + * @brief A hash function for a text reference + */ + template + struct hash > + { + size_t operator() (const db::text_ref &o) const + { + return std::hash > () (o); + } + }; + /** * @brief A hash value for a db::LayerProperties object */ @@ -328,6 +388,22 @@ namespace std return hfunc (hf2 (p.second), h); } }; + + /** + * @brief Generic hash for an unordered set + */ + template + struct hash > + { + size_t operator() (const std::unordered_set &o) const + { + size_t hf = 0; + for (typename std::unordered_set::const_iterator i = o.begin (); i != o.end (); ++i) { + hf = hfunc (hf, std::hash () (*i)); + } + return hf; + } + }; } #endif diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index c99b49440..537d52967 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -892,7 +892,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, } } -// @@@ can we shortcut this if interactions is empty? +// TODO: can we shortcut this if interactions is empty? { db::box_scanner2 scanner; InteractionRegistrationInst2Inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.intruder_layer (), dist, &interactions); @@ -941,7 +941,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, scanner.process (rec, dist, inst_bcs, inst_bci); } -// @@@ can we shortcut this if interactions is empty? +// TODO: can we shortcut this if interactions is empty? { db::box_scanner2 scanner; InteractionRegistrationInst2Shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); @@ -1143,7 +1143,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: scanner.insert (ref, id++); } -// @@@ TODO: can we confine this search to the subject's (sized) bounding box? +// TODO: TODO: can we confine this search to the subject's (sized) bounding box? for (std::unordered_set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert (i.operator-> (), interactions.next_id ()); } @@ -1161,13 +1161,13 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: scanner.insert1 (ref, id++); } -// @@@ TODO: can we confine this search to the subject's (sized) bounding box? +// TODO: can we confine this search to the subject's (sized) bounding box? for (std::unordered_set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert2 (i.operator-> (), interactions.next_id ()); } if (intruder_shapes) { -// @@@ TODO: can we confine this search to the subject's (sized) bounding box? +// TODO: can we confine this search to the subject's (sized) bounding box? for (db::Shapes::shape_iterator i = intruder_shapes->begin (polygon_ref_flags ()); !i.at_end (); ++i) { scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), interactions.next_id ()); } @@ -1198,7 +1198,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: // interactions low in the hierarchy. } else if (intruder_cell) { -// @@@ TODO: can we confine this search to the subject's (sized) bounding box? +// TODO: can we confine this search to the subject's (sized) bounding box? for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { if (! inst_bci (i->cell_inst ()).empty ()) { scanner.insert2 (&i->cell_inst (), ++inst_id); @@ -1206,7 +1206,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: } } -// @@@ TODO: can we confine this search to the subject's (sized) bounding box? +// TODO: can we confine this search to the subject's (sized) bounding box? for (std::unordered_set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { if (! inst_bci (*i).empty ()) { scanner.insert2 (i.operator-> (), ++inst_id); diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index 5bf25384b..7b6cae0a0 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -39,69 +39,6 @@ #include "dbHash.h" -// @@@ should go into dbHash.h -namespace std -{ - template - struct hash > - { - size_t operator() (const db::disp_trans &t) const - { - return hfunc (t.disp ()); - } - }; - - template - struct hash > - { - size_t operator() (const db::shape_ref &o) const - { - return hfunc (std::hash () (*o.ptr ()), std::hash () (o.trans ())); - } - }; - - template - struct hash > - { - size_t operator() (const db::polygon_ref &o) const - { - return std::hash > () (o); - } - }; - - template - struct hash > - { - size_t operator() (const db::path_ref &o) const - { - return std::hash > () (o); - } - }; - - template - struct hash > - { - size_t operator() (const db::text_ref &o) const - { - return std::hash > () (o); - } - }; - - template - struct hash > - { - size_t operator() (const std::unordered_set &o) const - { - size_t hf = 0; - for (typename std::unordered_set::const_iterator i = o.begin (); i != o.end (); ++i) { - hf = hfunc (hf, std::hash () (*i)); - } - return hf; - } - }; -} - - namespace db { From 707c761bacff8aa063f99d19da60b79f05218c38 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 25 Jan 2019 00:21:01 +0100 Subject: [PATCH 211/335] WIP: local hierarchical operations: take boolean core rather than shape ref core -> better hierarchical quality. Tests need to be fixed. --- src/db/db/db.pro | 6 +- src/db/db/dbHierProcessor.cc | 63 +++++++++++++++--- src/db/db/dbLocalOperation.cc | 67 +------------------ src/db/db/dbLocalOperationUtils.cc | 70 ++++++++++++++++++++ src/db/db/dbLocalOperationUtils.h | 78 +++++++++++++++++++++++ src/db/unit_tests/dbHierProcessorTests.cc | 21 ++++++ 6 files changed, 227 insertions(+), 78 deletions(-) create mode 100644 src/db/db/dbLocalOperationUtils.cc create mode 100644 src/db/db/dbLocalOperationUtils.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 0c9fcd68f..3a99dcb25 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -162,7 +162,8 @@ SOURCES = \ dbLayoutToNetlistReader.cc \ dbLayoutToNetlistWriter.cc \ dbLayoutToNetlistFormatDefs.cc \ - dbDeviceAbstract.cc + dbDeviceAbstract.cc \ + dbLocalOperationUtils.cc HEADERS = \ dbArray.h \ @@ -289,7 +290,8 @@ HEADERS = \ dbLayoutToNetlistReader.h \ dbLayoutToNetlistWriter.h \ dbLayoutToNetlistFormatDefs.h \ - dbDeviceAbstract.h + dbDeviceAbstract.h \ + dbLocalOperationUtils.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 537d52967..4e652c26b 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -27,6 +27,7 @@ #include "dbBoxConvert.h" #include "dbEdgeProcessor.h" #include "dbPolygonGenerators.h" +#include "dbLocalOperationUtils.h" #include "tlLog.h" #include "tlTimer.h" #include "tlInternational.h" @@ -253,6 +254,43 @@ LocalProcessorCellContexts::create (const key_type &intruders) return &m_contexts[intruders]; } +static void +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout) +{ + if (other.empty ()) { + return; + } + + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + + for (std::unordered_set::const_iterator i = res.begin (); i != res.end (); ++i) { + const db::PolygonRef &subject = *i; + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + for (std::unordered_set::const_iterator i = other.begin (); i != other.end (); ++i) { + const db::PolygonRef &subject = *i; + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + double m_max_area_ratio = 3.0; // @@@ + size_t m_max_vertex_count = 16; // @@@ + res.clear (); + db::BooleanOp op (db::BooleanOp::ANotB); + db::PolygonRefGenerator pr (layout, res); + db::PolygonSplitter splitter (pr, m_max_area_ratio, m_max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); + ep.process (pg, op); +} + void LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, const LocalProcessor *proc) { @@ -313,16 +351,13 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte if (! lost.empty ()) { - std::unordered_set new_common; - for (std::unordered_set::const_iterator i = common.begin (); i != common.end (); ++i) { - if (res.find (*i) != res.end ()) { - new_common.insert (*i); - } - } - common.swap (new_common); + subtract (lost, res, cell->layout ()); - for (std::unordered_map::iterator cc = m_contexts.begin (); cc != c; ++cc) { - cc->second.propagate (lost); + if (! lost.empty ()) { + subtract (common, lost, cell->layout ()); + for (std::unordered_map::iterator cc = m_contexts.begin (); cc != c; ++cc) { + cc->second.propagate (lost); + } } } @@ -334,7 +369,15 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte } } - c->second.propagate (gained); + if (! gained.empty ()) { + + subtract (gained, common, cell->layout ()); + + if (! gained.empty ()) { + c->second.propagate (gained); + } + + } } diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index acedde581..362a80caa 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -28,6 +28,7 @@ #include "dbEdgeProcessor.h" #include "dbPolygonGenerators.h" #include "dbPolygonTools.h" +#include "dbLocalOperationUtils.h" #include "tlLog.h" #include "tlTimer.h" #include "tlInternational.h" @@ -35,75 +36,9 @@ namespace db { - // --------------------------------------------------------------------------------------------- // BoolAndOrNotLocalOperation implementation -namespace { - -class PolygonRefGenerator - : public PolygonSink -{ -public: - /** - * @brief Constructor specifying an external vector for storing the polygons - */ - PolygonRefGenerator (db::Layout *layout, std::unordered_set &polyrefs) - : PolygonSink (), mp_layout (layout), mp_polyrefs (&polyrefs) - { } - - /** - * @brief Implementation of the PolygonSink interface - */ - virtual void put (const db::Polygon &polygon) - { - tl::MutexLocker locker (&mp_layout->lock ()); - mp_polyrefs->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); - } - -private: - db::Layout *mp_layout; - std::unordered_set *mp_polyrefs; -}; - -class PolygonSplitter - : public PolygonSink -{ -public: - PolygonSplitter (PolygonSink &sink, double max_area_ratio, size_t max_vertex_count) - : mp_sink (&sink), m_max_area_ratio (max_area_ratio), m_max_vertex_count (max_vertex_count) - { - // .. nothing yet .. - } - - virtual void put (const db::Polygon &poly) - { - if ((m_max_vertex_count > 0 && poly.vertices () > m_max_vertex_count) || (m_max_area_ratio > 0.0 && poly.area_ratio () > m_max_area_ratio)) { - - std::vector split_polygons; - db::split_polygon (poly, split_polygons); - for (std::vector ::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) { - put (*sp); - } - - } else { - mp_sink->put (poly); - } - } - - virtual void start () { mp_sink->start (); } - virtual void flush () { mp_sink->flush (); } - -private: - PolygonSink *mp_sink; - double m_max_area_ratio; - size_t m_max_vertex_count; -}; - -} - -// --------------------------------------------------------------------------------------------- - BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and, double max_area_ratio, size_t max_vertex_count) : m_is_and (is_and), m_max_area_ratio (max_area_ratio), m_max_vertex_count (max_vertex_count) { diff --git a/src/db/db/dbLocalOperationUtils.cc b/src/db/db/dbLocalOperationUtils.cc new file mode 100644 index 000000000..04fe0c747 --- /dev/null +++ b/src/db/db/dbLocalOperationUtils.cc @@ -0,0 +1,70 @@ + +/* + + 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 "dbLocalOperationUtils.h" +#include "dbPolygonTools.h" + +namespace db +{ + +// ----------------------------------------------------------------------------------------------- +// class PolygonRefGenerator + +PolygonRefGenerator::PolygonRefGenerator (db::Layout *layout, std::unordered_set &polyrefs) + : PolygonSink (), mp_layout (layout), mp_polyrefs (&polyrefs) +{ + // .. nothing yet .. +} + +void PolygonRefGenerator::put (const db::Polygon &polygon) +{ + tl::MutexLocker locker (&mp_layout->lock ()); + mp_polyrefs->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); +} + +// ----------------------------------------------------------------------------------------------- +// class PolygonSplitter + +PolygonSplitter::PolygonSplitter (PolygonSink &sink, double max_area_ratio, size_t max_vertex_count) + : mp_sink (&sink), m_max_area_ratio (max_area_ratio), m_max_vertex_count (max_vertex_count) +{ + // .. nothing yet .. +} + +void +PolygonSplitter::put (const db::Polygon &poly) +{ + if ((m_max_vertex_count > 0 && poly.vertices () > m_max_vertex_count) || (m_max_area_ratio > 0.0 && poly.area_ratio () > m_max_area_ratio)) { + + std::vector split_polygons; + db::split_polygon (poly, split_polygons); + for (std::vector ::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) { + put (*sp); + } + + } else { + mp_sink->put (poly); + } +} + +} diff --git a/src/db/db/dbLocalOperationUtils.h b/src/db/db/dbLocalOperationUtils.h new file mode 100644 index 000000000..ddcfa4ecc --- /dev/null +++ b/src/db/db/dbLocalOperationUtils.h @@ -0,0 +1,78 @@ + +/* + + 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_dbLocalOperationUtils +#define HDR_dbLocalOperationUtils + +#include "dbCommon.h" + +#include "dbLayout.h" +#include "dbPolygonGenerators.h" +#include "dbHash.h" + +#include + +namespace db +{ + +class PolygonRefGenerator + : public PolygonSink +{ +public: + /** + * @brief Constructor specifying an external vector for storing the polygons + */ + PolygonRefGenerator (db::Layout *layout, std::unordered_set &polyrefs); + + /** + * @brief Implementation of the PolygonSink interface + */ + virtual void put (const db::Polygon &polygon); + +private: + db::Layout *mp_layout; + std::unordered_set *mp_polyrefs; +}; + +class PolygonSplitter + : public PolygonSink +{ +public: + PolygonSplitter (PolygonSink &sink, double max_area_ratio, size_t max_vertex_count); + + virtual void put (const db::Polygon &poly); + + virtual void start () { mp_sink->start (); } + virtual void flush () { mp_sink->flush (); } + +private: + PolygonSink *mp_sink; + double m_max_area_ratio; + size_t m_max_vertex_count; +}; + +} + +#endif + diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index a457db90d..217acfc6b 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -1070,3 +1070,24 @@ TEST(TopWithBelow2) { run_test_bool (_this, "hlp12.oas", TMNotSwapped, 101); } + +TEST(BasicHierarchyVariantsAnd) +{ + run_test_bool (_this, "hlp13.oas", TMAnd, 100); +} + +TEST(BasicHierarchyVariantsNot) +{ + run_test_bool (_this, "hlp13.oas", TMNot, 101); +} + +TEST(BasicHierarchyVariantsAnd2) +{ + run_test_bool (_this, "hlp14.oas", TMAnd, 100); +} + +TEST(BasicHierarchyVariantsNot2) +{ + run_test_bool (_this, "hlp14.oas", TMNot, 101); +} + From 6da9bc5e85c70742527c20f01f314a18cf17f795 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 25 Jan 2019 21:38:45 +0100 Subject: [PATCH 212/335] Updated tests after switching to boolean core. --- src/db/unit_tests/dbHierProcessorTests.cc | 8 ++++---- testdata/algo/device_extract_au1_flat.gds | Bin 0 -> 78534 bytes testdata/algo/hlp10.oas | Bin 607 -> 635 bytes testdata/algo/hlp13.oas | Bin 0 -> 470 bytes testdata/algo/hlp14.oas | Bin 0 -> 526 bytes testdata/algo/hlp3.oas | Bin 968 -> 937 bytes testdata/algo/hlp4.oas | Bin 984 -> 957 bytes testdata/algo/hlp5.oas | Bin 1141 -> 1098 bytes testdata/algo/hlp6.oas | Bin 1819 -> 2671 bytes testdata/algo/hlp8.oas | Bin 1286 -> 1275 bytes 10 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 testdata/algo/device_extract_au1_flat.gds create mode 100644 testdata/algo/hlp13.oas create mode 100644 testdata/algo/hlp14.oas diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 217acfc6b..97ff2dfce 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -704,13 +704,13 @@ TEST(TwoInputsNot5) TEST(TwoInputsAnd6) { // Extreme variants (copy, vanishing), AND - run_test_bool2 (_this, "hlp6.oas", TMAnd, 100); + run_test_bool2 (_this, "hlp6.oas", TMAnd, 120); } TEST(TwoInputsNot6) { // Extreme variants (copy, vanishing), NOT - run_test_bool2 (_this, "hlp6.oas", TMNot, 101); + run_test_bool2 (_this, "hlp6.oas", TMNot, 121); } TEST(TwoInputsAnd7) @@ -844,13 +844,13 @@ TEST(TwoInputsNotWithSize5) TEST(TwoInputsAndWithSize6) { // Extreme variants (copy, vanishing), AND - run_test_bool2_with_size (_this, "hlp6.oas", TMAnd, 1500, 102); + run_test_bool2_with_size (_this, "hlp6.oas", TMAnd, 1500, 122); } TEST(TwoInputsNotWithSize6) { // Extreme variants (copy, vanishing), NOT - run_test_bool2_with_size (_this, "hlp6.oas", TMNot, 1500, 103); + run_test_bool2_with_size (_this, "hlp6.oas", TMNot, 1500, 123); } TEST(TwoInputsAndWithSize7) diff --git a/testdata/algo/device_extract_au1_flat.gds b/testdata/algo/device_extract_au1_flat.gds new file mode 100644 index 0000000000000000000000000000000000000000..1dc09360f3a9067942bd0a5ebeb10323bd831ad2 GIT binary patch literal 78534 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 W{hR4~SpR1Fv8;b5*khgXt^Wrh`6bT) literal 0 HcmV?d00001 diff --git a/testdata/algo/hlp10.oas b/testdata/algo/hlp10.oas index 0da2e135c7b5d17fbc1b0e98dee66cf660fc0c3b..ca1e4a913b138ad00c5aeaf9c8860b7588bbc744 100644 GIT binary patch delta 188 zcmcc5@|$JCdRqfVrVjNFYzrAh4bwo(6WmLfMJ@9gnYL)o;oQYCol(>>52EA(D}$(M zI*2`ksfUr9;RBn%#GNWRf~k8Rv51<2H8-%%`M|;;Y64Q!!nEZ(kZlV#>H+VoS1etb z+)P{>*e@_|U>CIo8+wB6!zY#nSzxmux_0n3yaDPm1l!ZV3Njk(!WO14lUSK1Co=wF IZeU;l0I|w9+yDRo delta 164 zcmey(a-U_wdM?8>My3w+6WmLfC%#dsH%tZbj&SaI#3E_}Vt!!z@QH;%)D*0$fpyLY zAO|eb!nEZ(NFp7iY6jDsuRx9gh&_kp&IDEna|S!e9K$?_K?mAdAzF9vHoO6eu1rxC5QpWD7z5MfM#dkLCor-yF)#oClZ`RI diff --git a/testdata/algo/hlp13.oas b/testdata/algo/hlp13.oas new file mode 100644 index 0000000000000000000000000000000000000000..236c9dc3297b07caf9fd9d6a60d91a38dba9e95d GIT binary patch literal 470 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4j84pqj?5q?FQce6BLgGT29^yh zTNqCWh-xr0-C${9-y$HYnFqWSJt;$hCxdkH!o37wo)Df*%=naO|-W X{KGhhbAdcJ1DE(WCZ>jw3>X*yr2Md} literal 0 HcmV?d00001 diff --git a/testdata/algo/hlp14.oas b/testdata/algo/hlp14.oas new file mode 100644 index 0000000000000000000000000000000000000000..47926a477c0a39c764c79b8ec432ccf71c9be215 GIT binary patch literal 526 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4jE>A8nwLS;nu&pt>C|QBA5&Q8 z8ZP_FBC5m4bnYkPj-(k{+zbn5G6{%UGBPr?RnO46B>!wNlc)tF4^XcS0|2yb&;I}b literal 0 HcmV?d00001 diff --git a/testdata/algo/hlp3.oas b/testdata/algo/hlp3.oas index e0574ba5f37d892be4dc5656bb55e6256fe4c69a..e62f42b524f319d584b57515eb4ac60eba268617 100644 GIT binary patch delta 220 zcmX@XzLI^yElC4LmIFK=MJLD{Q2ih(YM8>vbb#jv7lWw5#5?_*VCDig5J!1(AEQG3 z59JL?ce(eniW)F7bvT@0?3u&}VNNjpG84-Dq5MP=BKU&+iytqe;17lgGCRb%84lP9 ziCU*JKy2txzacBinQ~(mqbOsZsAWDQ(*;MMdebF^q6&;mH)LP1i&`);{ZO93{KV)_ zAET%ZBhv~uu?fseb~4@NW&m+N=xyNa`OI{On_;phlN%$`n+a delta 268 zcmZ3!aRJ-JKT?y|F)Gw|IGkYYnZ&r4Rn&ly zsl#D{>6e*|5atW^FMhm?fdedK}3NH5TYWrOjjnSGdV?xTIMq{U2p_i zXu6~jXd=@Obxlm+WM^%gq4N m(c$nz`HLw~2Tfyi9^W7$(T<5a(t%U?(K_f$=9$CD0v=OgChK2AeJ^6t!Sv`k_35`H9h=K1NX+ zMy3^R6PTCmWV*}EFnI-&fw-t;D#XMN^&7IHoGCYEF^V$gF-?BR^mXzFCJrVB1^|N? BMJNCO delta 254 zcmdnXeuI6&UJj9LMurB-6(Wn8jq62VyddTINqYC|}>== c(5*j|znK0JoLtTXbou0iOutwg8X6cF0Ax>5`~Uy| diff --git a/testdata/algo/hlp5.oas b/testdata/algo/hlp5.oas index 04963b4004a1fbe601de1936ebd4f499d832c79f..0f4c59bc6d3ce119d0782fd2ea94a88bfde9ff56 100644 GIT binary patch delta 298 zcmey$af)NYV@U%>mIFK=MJLD{Q2ih(YM8>vbb#jv7lWw5#HZa{ig^$&h^I7p3ZtUN z59JL?ce(eniW)F7bvT@0?3u&}VNNjpG84-Dq5MR0@<~S70I*RFMmJ`*^2p9$HLFQz@;7}0Hc uV)SG(6Ua)CElYMXfs_DwAX|DqGacz=n*5*1nvrR8BlD-p6PUS~7#IN3$YKis delta 344 zcmX@b@s(r3VTsA~`ei2L( zQ3kBWhLLH7+XUu0N0{z%Gk|n-IQ&q4;{enFl=+~yfwScw(;aRGUM9g03=?D)%mmVm zKe-r0ty395ZiP6$!RWwzCZJ=vA#R)?Bg&XJnV;F(2JW6Oraj*nMHMg|0SXY11D5P$ c0@)6-vF9_>kzOXI$$`w@CnqqoF)=Uz08fr{m;e9( diff --git a/testdata/algo/hlp6.oas b/testdata/algo/hlp6.oas index 38a6ae94af595d277825cb027aed8ecd62aeea76..92c5b9f8295958e2274437000c203da39779502c 100644 GIT binary patch delta 943 zcmZ9K(MwZN9LLW&_ikgmbE$OLgOh5*9E?PV8xCj89)dvn6M{-9=)nw%RLBuNTxrE! z*M}^jQ9c+5-I2%@6s}}6#D~#C+=qfa>|qZ=lO#?sr1Lv>6Y1r055M2}em}qO`MnAJ zI;)vCX=)M^HB-X5mw4MnhcKzc3~%E*`JuGo4u;fTGpHczawi?0M3|IEYo3G5$`mx# z`!s2Jm;6-Vz#>L?t*g;N(^xixhPM)9>G@->qA!u;^+E8cc^VH9?T23$Imo~*GiWE* zzLLcP<|nz9(xhV8PIA%32#sQ5-_<1Ul?E@vE94LznJDhPz$7uNAL5H-WJZ6F$NU4+ zm{@-1%f@LszZbtYh-J%vNVSmF!_tT*9y?{VSHa^?efKE`Do5>ZdmlT)af?c<-d9KPhC!|x-+v`JQxx3Qj{q)GIZEXIh$)P)npp@Gz3W?K zc}Hqxv)&HZPKA^EO?ICX1+8Dv^RDxA#_+)d=-_IrSNYVN1mrk~n$Z&T8@Q?y1k@1Y z{5?L!H($XA-4NzcqInhaN4aN~`_84lP9iQ1I9vWP-Qi~7WdhniL1w{BCT_+r%#)ZNF$#WQ z{K>^2Y7KOWJ*yz&WMPg1o^+sNG!Ceqs5>y9Y4SC;y;d;ieKGC%#wcol-F-`TGJz}u zIcUy0#wU}RxEXpXCog1C$o|pJ*phu_1~bU*E8He9166{|0rDPbZ{Y0t%rs>>i>L-8 z+Ya%UmM<)4ICt{>uvs$+th~cvf@#Y(kZC|`fo8v8Z^;H}>~PrOxkUhKCdhw4Q;zg9 OF-;4s(Ckk;FJ-hfeCc}Hg~KwIG_ z&eeh1imebdtSanh4G=@UnReMpu&@&cqgbJlqH0J??Jg@5dwD_)fe;b{>>}6oZvabN z^8{D(-zvZR38&`CCzpWJ{9$Ui1k}yPvk;cS`h4o9W*1q-X;j{mc=YHQu)N9J2WV=3 z8Kru|zXe`yD?Q1Xaq|Sr+w3#3nKm`2hSHYv2sj&&1N@(x68_i&8eE72{JH=FRK@b(^dwn5wf-C6&cZ;WiOb{jBNLRRDJGQ7b>@)}6_7_cu4vR;#vk3fiwLDpwuoPfO& z%VM3CPTbiezK)311?)8$N^K&OBBXtNY(@q(Z7njc8ji-U&hkB5bF@ITHIDQk1>&P* zYy5n8#=260_0BT9CN%>wlUPs+ad;=poyH0e@JfuRS6`51U{hdy+T+?6PNc J^Fk6M^bK~npVj~X From 12aaa2db200df2d3dcadf659d50154a787ea8222 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 25 Jan 2019 21:48:56 +0100 Subject: [PATCH 213/335] Refactoring: unified handling of splitting parameters. --- src/db/db/dbDeepRegion.cc | 4 +++- src/db/db/dbHierProcessor.cc | 18 +++++++-------- src/db/db/dbHierProcessor.h | 27 +++++++++++++++++++++++ src/db/db/dbLocalOperation.cc | 10 ++++----- src/db/db/dbLocalOperation.h | 10 ++++----- src/db/unit_tests/dbHierProcessorTests.cc | 8 +++---- 6 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 17535ebad..3985efc92 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -346,10 +346,12 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const { DeepLayer dl_out (m_deep_layer.derived ()); - db::BoolAndOrNotLocalOperation op (and_op, m_deep_layer.store ()->max_area_ratio (), m_deep_layer.store ()->max_vertex_count ()); + db::BoolAndOrNotLocalOperation op (and_op); db::LocalProcessor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ()); proc.set_threads (m_deep_layer.store ()->threads ()); + proc.set_area_ratio(m_deep_layer.store ()->max_area_ratio ()); + proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); proc.run (&op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 4e652c26b..21729cb86 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -255,7 +255,7 @@ LocalProcessorCellContexts::create (const key_type &intruders) } static void -subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout) +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout, size_t max_vertex_count, double area_ratio) { if (other.empty ()) { return; @@ -281,12 +281,10 @@ subtract (std::unordered_set &res, const std::unordered_setlayout ()); + subtract (lost, res, cell->layout (), proc->max_vertex_count (), proc->area_ratio ()); if (! lost.empty ()) { - subtract (common, lost, cell->layout ()); + subtract (common, lost, cell->layout (), proc->max_vertex_count (), proc->area_ratio ()); for (std::unordered_map::iterator cc = m_contexts.begin (); cc != c; ++cc) { cc->second.propagate (lost); } @@ -371,7 +369,7 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte if (! gained.empty ()) { - subtract (gained, common, cell->layout ()); + subtract (gained, common, cell->layout (), proc->max_vertex_count (), proc->area_ratio ()); if (! gained.empty ()) { c->second.propagate (gained); @@ -782,13 +780,13 @@ LocalProcessorResultComputationTask::perform () // LocalProcessor implementation LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top) - : mp_subject_layout (layout), mp_intruder_layout (layout), mp_subject_top (top), mp_intruder_top (top), m_nthreads (0) + : mp_subject_layout (layout), mp_intruder_layout (layout), mp_subject_top (top), mp_intruder_top (top), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0) { // .. nothing yet .. } LocalProcessor::LocalProcessor (db::Layout *subject_layout, db::Cell *subject_top, const db::Layout *intruder_layout, const db::Cell *intruder_top) - : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), mp_subject_top (subject_top), mp_intruder_top (intruder_top), m_nthreads (0) + : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), mp_subject_top (subject_top), mp_intruder_top (intruder_top), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0) { // .. nothing yet .. } @@ -1260,7 +1258,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: } - op->compute_local (mp_subject_layout, interactions, result); + op->compute_local (mp_subject_layout, interactions, result, m_max_vertex_count, m_area_ratio); } } diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index 7b6cae0a0..4cba12d1b 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -329,6 +329,31 @@ public: m_nthreads = nthreads; } + unsigned int threads () const + { + return m_nthreads; + } + + void set_max_vertex_count (size_t max_vertex_count) + { + m_max_vertex_count = max_vertex_count; + } + + size_t max_vertex_count () const + { + return m_max_vertex_count; + } + + void set_area_ratio (double area_ratio) + { + m_area_ratio = area_ratio; + } + + double area_ratio () const + { + return m_area_ratio; + } + private: friend class LocalProcessorCellContexts; friend class LocalProcessorContextComputationTask; @@ -339,6 +364,8 @@ private: const db::Cell *mp_intruder_top; std::string m_description; unsigned int m_nthreads; + size_t m_max_vertex_count; + double m_area_ratio; mutable std::auto_ptr > mp_cc_job; std::string description (const LocalOperation *op) const; diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index 362a80caa..665b1817c 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -39,8 +39,8 @@ namespace db // --------------------------------------------------------------------------------------------- // BoolAndOrNotLocalOperation implementation -BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and, double max_area_ratio, size_t max_vertex_count) - : m_is_and (is_and), m_max_area_ratio (max_area_ratio), m_max_vertex_count (max_vertex_count) +BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and) + : m_is_and (is_and) { // .. nothing yet .. } @@ -58,7 +58,7 @@ BoolAndOrNotLocalOperation::description () const } void -BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const +BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const { db::EdgeProcessor ep; @@ -103,7 +103,7 @@ BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const ShapeIntera db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); db::PolygonRefGenerator pr (layout, result); - db::PolygonSplitter splitter (pr, m_max_area_ratio, m_max_vertex_count); + db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); db::PolygonGenerator pg (splitter, true, true); ep.process (pg, op); @@ -118,7 +118,7 @@ SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wra // .. nothing yet .. } -void SelfOverlapMergeLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const +void SelfOverlapMergeLocalOperation::compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const { if (m_wrap_count == 0) { return; diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h index 73c2ba242..0ce915e83 100644 --- a/src/db/db/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -88,7 +88,7 @@ public: * @param interactions The interaction set * @param result The container to which the results are written */ - virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const = 0; + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const = 0; /** * @brief Indicates the desired behaviour when a shape does not have an intruder @@ -114,16 +114,14 @@ class DB_PUBLIC BoolAndOrNotLocalOperation : public LocalOperation { public: - BoolAndOrNotLocalOperation (bool is_and, double max_area_ratio = 0.0, size_t max_vertex_count = 0); + BoolAndOrNotLocalOperation (bool is_and); - virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const; + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const; virtual on_empty_intruder_mode on_empty_intruder_hint () const; virtual std::string description () const; private: bool m_is_and; - double m_max_area_ratio; - size_t m_max_vertex_count; }; /** @@ -137,7 +135,7 @@ class DB_PUBLIC SelfOverlapMergeLocalOperation public: SelfOverlapMergeLocalOperation (unsigned int wrap_count); - virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result) const; + virtual void compute_local (db::Layout *layout, const ShapeInteractions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const; virtual on_empty_intruder_mode on_empty_intruder_hint () const; virtual std::string description () const; diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 97ff2dfce..a9d117c98 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -55,7 +55,7 @@ public: // .. nothing yet .. } - virtual void compute_local (db::Layout *layout, const db::ShapeInteractions &interactions, std::unordered_set &result) const + virtual void compute_local (db::Layout *layout, const db::ShapeInteractions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const { db::ShapeInteractions sized_interactions = interactions; for (db::ShapeInteractions::iterator i = sized_interactions.begin (); i != sized_interactions.end (); ++i) { @@ -66,7 +66,7 @@ public: sized_interactions.add_shape (*j, db::PolygonRef (poly, layout->shape_repository ())); } } - BoolAndOrNotLocalOperation::compute_local (layout, sized_interactions, result); + BoolAndOrNotLocalOperation::compute_local (layout, sized_interactions, result, max_vertex_count, area_ratio); } db::Coord dist () const @@ -91,7 +91,7 @@ public: // .. nothing yet .. } - virtual void compute_local (db::Layout *layout, const db::ShapeInteractions &interactions, std::unordered_set &result) const + virtual void compute_local (db::Layout *layout, const db::ShapeInteractions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const { db::ShapeInteractions sized_interactions = interactions; for (db::ShapeInteractions::iterator i = sized_interactions.begin (); i != sized_interactions.end (); ++i) { @@ -110,7 +110,7 @@ public: } - SelfOverlapMergeLocalOperation::compute_local (layout, sized_interactions, result); + SelfOverlapMergeLocalOperation::compute_local (layout, sized_interactions, result, max_vertex_count, area_ratio); } db::Coord dist () const From 29264013b0ff42e87a4c599dd8031265c133691c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 25 Jan 2019 22:28:25 +0100 Subject: [PATCH 214/335] WIP: more consistent handling of polygon splitting parameters. --- src/db/db/db.pro | 3 +- src/db/db/dbDeepRegion.cc | 2 +- src/db/db/dbDeepRegion.h | 4 +- src/db/db/gsiDeclDbDeepShapeStore.cc | 101 +++++++++++++++++++++ src/db/db/gsiDeclDbHierNetworkProcessor.cc | 2 +- src/db/db/gsiDeclDbRegion.cc | 30 +----- src/db/unit_tests/dbHierProcessorTests.cc | 4 + testdata/algo/hlp11.oas | Bin 823 -> 834 bytes testdata/algo/hlp12.oas | Bin 798 -> 868 bytes 9 files changed, 112 insertions(+), 34 deletions(-) create mode 100644 src/db/db/gsiDeclDbDeepShapeStore.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 3a99dcb25..0b2eaeaeb 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -163,7 +163,8 @@ SOURCES = \ dbLayoutToNetlistWriter.cc \ dbLayoutToNetlistFormatDefs.cc \ dbDeviceAbstract.cc \ - dbLocalOperationUtils.cc + dbLocalOperationUtils.cc \ + gsiDeclDbDeepShapeStore.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 3985efc92..f05396ff5 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -350,7 +350,7 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const db::LocalProcessor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ()); proc.set_threads (m_deep_layer.store ()->threads ()); - proc.set_area_ratio(m_deep_layer.store ()->max_area_ratio ()); + proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ()); proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); proc.run (&op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 2d9a1e509..720782399 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -42,8 +42,8 @@ public: typedef polygon_layer_type::iterator polygon_iterator_type; DeepRegion (); - DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio = 3.0, size_t max_vertex_count = 16); - DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 3.0, size_t max_vertex_count = 16); + DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio = 0.0, size_t max_vertex_count = 0); + DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 0.0, size_t max_vertex_count = 0); DeepRegion (const DeepRegion &other); DeepRegion (const DeepLayer &dl); diff --git a/src/db/db/gsiDeclDbDeepShapeStore.cc b/src/db/db/gsiDeclDbDeepShapeStore.cc new file mode 100644 index 000000000..77ac2fc30 --- /dev/null +++ b/src/db/db/gsiDeclDbDeepShapeStore.cc @@ -0,0 +1,101 @@ + +/* + + 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 "gsiDecl.h" +#include "dbDeepShapeStore.h" + +namespace gsi +{ + +Class decl_dbDeepShapeStore ("db", "DeepShapeStore", + gsi::method ("instance_count", &db::DeepShapeStore::instance_count, + "@hide\n" + ) + + gsi::method ("threads=", &db::DeepShapeStore::set_threads, gsi::arg ("n"), + "@brief Sets the number of threads to allocate for the hierarchical processor\n" + ) + + gsi::method ("threads", &db::DeepShapeStore::threads, + "@brief Gets the number of threads.\n" + ) + + gsi::method ("max_vertex_count=", &db::DeepShapeStore::set_max_vertex_count, gsi::arg ("count"), + "@brief Sets the maximum vertex count default value\n" + "\n" + "This parameter is used to simplify complex polygons. It is used by\n" + "create_polygon_layer with the default parameters. It's also used by\n" + "boolean operations when they deliver their output.\n" + ) + + gsi::method ("max_vertex_count", &db::DeepShapeStore::max_vertex_count, + "@brief Gets the maximum vertex count.\n" + ) + + gsi::method ("max_area_ratio=", &db::DeepShapeStore::set_max_area_ratio, gsi::arg ("ratio"), + "@brief Sets the max. area ratio for bounding box vs. polygon area\n" + "\n" + "This parameter is used to simplify complex polygons. It is used by\n" + "create_polygon_layer with the default parameters. It's also used by\n" + "boolean operations when they deliver their output.\n" + ) + + gsi::method ("max_area_ratio", &db::DeepShapeStore::max_area_ratio, + "@brief Gets the max. area ratio.\n" + ) + + gsi::method ("text_property_name=", &db::DeepShapeStore::set_text_property_name, gsi::arg ("name"), + "@brief Sets the text property name.\n" + "\n" + "If set to a non-null variant, text strings are attached to the generated boxes\n" + "as properties with this particular name. This option has an effect only if the\n" + "text_enlargement property is not negative.\n" + "By default, the name is empty.\n" + ) + + gsi::method ("text_property_name", &db::DeepShapeStore::text_property_name, + "@brief Gets the text property name.\n" + ) + + gsi::method ("text_enlargement=", &db::DeepShapeStore::set_text_enlargement, gsi::arg ("value"), + "@brief Sets the text enlargement value\n" + "\n" + "If set to a non-negative value, text objects are converted to boxes with the\n" + "given enlargement (width = 2 * enlargement). The box centers are identical\n" + "to the original location of the text.\n" + "If this value is negative (the default), texts are ignored.\n" + ) + + gsi::method ("text_enlargement", &db::DeepShapeStore::text_enlargement, + "@brief Gets the text enlargement value.\n" + ), + "@brief An opaque layout heap for the deep region processor\n" + "\n" + "This class is used for keeping intermediate, hierarchical data for the " + "deep region processor. It is used in conjunction with the region " + "constructor to create a deep (hierarchical) region." + "\n" + "@code\n" + "layout = ... # a layout\n" + "layer = ... # a layer\n" + "cell = ... # a cell (initial cell for the deep region)\n" + "dss = RBA::DeepShapeStore::new\n" + "region = RBA::Region::new(cell.begin(layer), dss)\n" + "@/code\n" + "\n" + "The DeepShapeStore object also supplies some configuration options " + "for the operations acting on the deep regions. See for example \\threads=.\n" + "\n" + "This class has been introduced in version 0.26.\n" +); + +} diff --git a/src/db/db/gsiDeclDbHierNetworkProcessor.cc b/src/db/db/gsiDeclDbHierNetworkProcessor.cc index a72f15d9c..8aef6b9d0 100644 --- a/src/db/db/gsiDeclDbHierNetworkProcessor.cc +++ b/src/db/db/gsiDeclDbHierNetworkProcessor.cc @@ -43,7 +43,7 @@ Class decl_dbConnectivity ("db", "Connectivity", gsi::method ("global_net_id", &db::Connectivity::global_net_id, gsi::arg ("global_net_name"), "@brief Gets the ID for a given global net name.\n" ), - "@brief This class specifies connections between different layers." + "@brief This class specifies connections between different layers.\n" "Connections are build using \\connect. There are basically two flavours of connections: intra-layer and inter-layer.\n" "\n" "Intra-layer connections make nets begin propagated along different shapes on the same net. Without the " diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 9d7d653d0..f5b1e6877 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -719,34 +719,6 @@ static Container *decompose_trapezoids (const db::Region *r, int mode) int td_simple (); int po_any (); -Class decl_DeepShapeStore ("db", "DeepShapeStore", - method ("threads=", &db::DeepShapeStore::set_threads, gsi::arg ("threads"), - "@brief Sets the number of threads for use for operations acting on this heap\n" - ) + - method ("threads", &db::DeepShapeStore::threads, - "@brief Gets the number of threads for use for operations acting on this heap\n" - ) + - method ("instance_count", db::DeepShapeStore::instance_count, "@hide"), - "@brief An opaque layout heap for the deep region processor\n" - "\n" - "This class is used for keeping intermediate, hierarchical data for the " - "deep region processor. It is used in conjunction with the region " - "constructor to create a deep (hierarchical) region." - "\n" - "@code\n" - "layout = ... # a layout\n" - "layer = ... # a layer\n" - "cell = ... # a cell (initial cell for the deep region)\n" - "dss = RBA::DeepShapeStore::new\n" - "region = RBA::Region::new(cell.begin(layer), dss)\n" - "@/code\n" - "\n" - "The DeepShapeStore object also supplies some configuration options " - "for the operations acting on the deep regions. See for example \\threads=.\n" - "\n" - "This class has been introduced in version 0.26.\n" -); - Class decl_Region ("db", "Region", constructor ("new", &new_v, "@brief Default constructor\n" @@ -824,7 +796,7 @@ Class decl_Region ("db", "Region", "r = RBA::Region::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + - constructor ("new", &new_sid, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("area_ratio", 3.0), gsi::arg ("max_vertex_count", size_t (16)), + constructor ("new", &new_sid, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("area_ratio", 0.0), gsi::arg ("max_vertex_count", size_t (0)), "@brief Constructor for a deep region from a hierarchical shape set\n" "\n" "This constructor creates a hierarchical region. Use a \\DeepShapeStore object to " diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index a9d117c98..060611d8c 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -226,6 +226,8 @@ static void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode m db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); proc.set_threads (nthreads); + proc.set_area_ratio (3.0); + proc.set_max_vertex_count (16); if (! context_doc) { proc.run (lop, l1, l2, lout); @@ -242,6 +244,8 @@ static void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode m db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &layout_org2, &layout_org2.cell (*layout_org2.begin_top_down ())); proc.set_threads (nthreads); + proc.set_area_ratio (3.0); + proc.set_max_vertex_count (16); if (! context_doc) { proc.run (lop, l1, l2, lout); diff --git a/testdata/algo/hlp11.oas b/testdata/algo/hlp11.oas index df75656981e7595538ed2b7f85ce2e77581cd18d..e509c377f49afbe21ae364b373bc1d5a68e28729 100644 GIT binary patch delta 192 zcmdnac8G04rKTY>o3n?fkBbpAbBKQcFQZ5`Bf}N33mQjsE^3G>rZciE2wb82lWQXL z#1{sF8H`K|*lx)7ii#S5SPe##Z5So^MJ*Vaekk8yd3%js2o~WCApHYiLq>!mWvPYC*@)|~UVMD0l4-PWI cO;=k!nVUD`FQkj_6#}n7GKmFP)KP zLEsAApIj4}kLZXRW-u}>V7npPD=KQh$TCCg2Fp+76}oS)F#@?ji36&0r-GR~#23gf zDdc4m{J`+$8sh~G!4HfRWOhtpoSe&O!zEJ5)F9a-%Fs|d`5B`+k7YU|(*=zKswe7h l$WGqEs3ifn<-tLaVQ_0ED=>*MGEEL-dOtaViI0ha0RS_xJ?H=c diff --git a/testdata/algo/hlp12.oas b/testdata/algo/hlp12.oas index 259bf940c8883554a5067d2aff61e96c3b5b7c9d..6abd3565c0c6fb75c4a40ee400dfd1ae11090ff3 100644 GIT binary patch delta 178 zcmbQo_JnQ1k9xinKKs~Ss^(v0&08t?g?WW!-UZhD7pnW%L@gMZUa02%2;CtBVs`L- z(CbiNU;$)vys{6h5%$454Zwo8o1idTw=8ZdHAP((821KS&kAE6-4Dk9d<4KKs~Ss^(v0&08t?g?WW!-UZhD7piY0R!XiHy2!dt=%wlx<_9ik cI0&oR9MHjcA*z$_G9ygwCC1Hj8ILmp04kg>a{vGU From 4712ee0f293029e7db28b463b65857d047ba88d1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 25 Jan 2019 22:40:41 +0100 Subject: [PATCH 215/335] Activated DeviceAbstract for GSI. --- src/db/db/gsiDeclDbNetlist.cc | 10 +++++++--- testdata/ruby/dbRegionTest.rb | 7 +++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index a44ae71d2..67954efcd 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -65,6 +65,10 @@ Class decl_dbDevice ("db", "Device", gsi::method ("device_class", &db::Device::device_class, "@brief Gets the device class the device belongs to.\n" ) + + gsi::method ("device_abstract", &db::Device::device_abstract, + "@brief Gets the device abstract for this device instance.\n" + "See \\DeviceAbstract for more details.\n" + ) + gsi::method ("circuit", (db::Circuit *(db::Device::*) ()) &db::Device::circuit, "@brief Gets the circuit the device lives in." ) + @@ -134,12 +138,13 @@ Class decl_dbDevice ("db", "Device", "This class has been added in version 0.26." ); -#if 0 -// TODO: activate once the geometry API is opened (clusters at al.) Class decl_dbDeviceAbstract ("db", "DeviceAbstract", gsi::method ("netlist", (db::Netlist *(db::DeviceAbstract::*) ()) &db::DeviceAbstract::netlist, "@brief Gets the netlist the device abstract lives in." ) + + gsi::method ("device_class", &db::DeviceAbstract::device_class, + "@brief Gets the device class of the device." + ) + gsi::method ("name=", &db::DeviceAbstract::set_name, gsi::arg ("name"), "@brief Sets the name of the device abstract.\n" "Device names are used to name a device abstract inside a netlist file. " @@ -162,7 +167,6 @@ Class decl_dbDeviceAbstract ("db", "DeviceAbstract", "\n" "This class has been added in version 0.26." ); -#endif static void subcircuit_connect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin, db::Net *net) { diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index b073b857f..37519e7dd 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -778,6 +778,13 @@ class DBRegion_TestClass < TestBase dss = RBA::DeepShapeStore::new dss._create assert_equal(RBA::DeepShapeStore::instance_count, 1) + # do a little testing on the DSS: + dss.max_vertex_count = 8 + assert_equal(dss.max_vertex_count, 8) + dss.max_area_ratio = 42.0 + assert_equal(dss.max_area_ratio, 42.0) + dss.threads = 3 + assert_equal(dss.threads, 3) dss = nil GC.start assert_equal(RBA::DeepShapeStore::instance_count, 0) From 74b63414259fc55896d046e9f0dc6ccea1e25717 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 25 Jan 2019 23:19:12 +0100 Subject: [PATCH 216/335] Fixed unit tests, added swap function to tl::list --- src/tl/tl/tlList.h | 21 ++++++++++++++++++++- src/tl/unit_tests/tlListTests.cc | 23 ++++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/tl/tl/tlList.h b/src/tl/tl/tlList.h index 2d09e868e..607fb27e7 100644 --- a/src/tl/tl/tlList.h +++ b/src/tl/tl/tlList.h @@ -28,6 +28,7 @@ #include "tlTypeTraits.h" #include +#include namespace tl { @@ -141,6 +142,24 @@ public: } } + void swap (list_impl &other) + { + std::swap (m_head.mp_next, other.m_head.mp_next); + if (m_head.mp_next) { + m_head.mp_next->mp_prev = &m_head; + } + if (other.m_head.mp_next) { + other.m_head.mp_next->mp_prev = &other.m_head; + } + std::swap (m_back.mp_prev, other.m_back.mp_prev); + if (m_back.mp_prev) { + m_back.mp_prev->mp_next = &m_back; + } + if (other.m_back.mp_prev) { + other.m_back.mp_prev->mp_next = &other.m_back; + } + } + bool empty () const { return m_head.mp_next == &m_back; @@ -303,7 +322,7 @@ public: list_impl () { } list_impl (const list_impl &other) - : list_impl (other) + : list_impl () { operator= (other); } diff --git a/src/tl/unit_tests/tlListTests.cc b/src/tl/unit_tests/tlListTests.cc index c6831c702..33e229a84 100644 --- a/src/tl/unit_tests/tlListTests.cc +++ b/src/tl/unit_tests/tlListTests.cc @@ -263,9 +263,24 @@ TEST(1_Basic) EXPECT_EQ (l2sr (l3), "1,17"); EXPECT_EQ (l3.size (), size_t (2)); + EXPECT_EQ (l2sr (l2), "17"); + EXPECT_EQ (l2sr (l3), "1,17"); + l3.swap (l2); + EXPECT_EQ (l2sr (l2), "1,17"); + EXPECT_EQ (l2sr (l3), "17"); + + l1.clear (); + l2.swap (l1); + EXPECT_EQ (l2sr (l1), "1,17"); + EXPECT_EQ (l2sr (l2), ""); + l1.clear (); - l2.clear (); l3.clear (); + + l2.swap (l1); + EXPECT_EQ (l2sr (l1), ""); + EXPECT_EQ (l2sr (l2), ""); + EXPECT_EQ (obj_count, size_t (0)); } @@ -383,6 +398,12 @@ TEST(2_BasicNoCopy) EXPECT_EQ (l2sr (l3), "1,17"); EXPECT_EQ (l3.size (), size_t (2)); + EXPECT_EQ (l2sr (l2), "17"); + EXPECT_EQ (l2sr (l3), "1,17"); + l3.swap (l2); + EXPECT_EQ (l2sr (l2), "1,17"); + EXPECT_EQ (l2sr (l3), "17"); + l1.clear (); l2.clear (); l3.clear (); From 863c6ba8dea2f8c67244ff3f313ced615f5c643d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Jan 2019 21:53:44 +0100 Subject: [PATCH 217/335] Fixed a hierarchy traversal bug in the hier processor. --- src/db/db/db.pro | 3 +- src/db/db/dbHierProcessor.cc | 55 ++++++++++++++++---- src/db/db/dbHierProcessor.h | 13 +++++ src/db/db/dbNetlistDeviceExtractorClasses.cc | 5 ++ 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 0b2eaeaeb..ef6d26aa5 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -292,7 +292,8 @@ HEADERS = \ dbLayoutToNetlistWriter.h \ dbLayoutToNetlistFormatDefs.h \ dbDeviceAbstract.h \ - dbLocalOperationUtils.h + dbLocalOperationUtils.h \ + dbDeepRegion.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 21729cb86..c84236f9d 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -772,6 +772,23 @@ LocalProcessorResultComputationTask::perform () // erase the contexts we don't need any longer { tl::MutexLocker locker (& mp_contexts->lock ()); + +#if defined(ENABLE_DB_HP_SANITY_ASSERTIONS) + std::set td; + for (db::LocalProcessorCellContexts::iterator i = mp_cell_contexts->begin (); i != mp_cell_contexts->end (); ++i) { + td.insert (&i->second); + } + for (db::LocalProcessorContexts::contexts_per_cell_type::iterator pcc = mp_contexts->context_map ().begin (); pcc != mp_contexts->context_map ().end (); ++pcc) { + for (db::LocalProcessorCellContexts::iterator i = pcc->second.begin (); i != pcc->second.end (); ++i) { + for (db::LocalProcessorCellContext::drop_iterator j = i->second.begin_drops (); j != i->second.end_drops (); ++j) { + if (td.find (j->parent_context) != td.end ()) { + tl_assert (false); + } + } + } + } +#endif + mp_contexts->context_map ().erase (mp_cell); } } @@ -892,6 +909,23 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::LocalProcessorCellContexts &cell_contexts = contexts.contexts_per_cell (subject_cell, intruder_cell); +#if defined(ENABLE_DB_HP_SANITY_ASSERTIONS) + if (subject_parent) { + db::LocalProcessorContexts::contexts_per_cell_type::iterator pcc = contexts.context_map ().find (subject_parent); + if (pcc == contexts.context_map ().end ()) { + tl_assert (false); + } + tl_assert (pcc->first == subject_parent); + bool any = false; + for (db::LocalProcessorCellContexts::iterator pcci = pcc->second.begin (); pcci != pcc->second.end () && !any; ++pcci) { + any = (&pcci->second == parent_context); + } + if (!any) { + tl_assert (false); + } + } + #endif + cell_context = cell_contexts.find_context (intruders); if (cell_context) { // we already have a context for this intruder scheme @@ -1087,23 +1121,24 @@ LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOp std::vector next_cells_bu; next_cells_bu.reserve (cells_bu.size ()); - std::list tasks; - for (std::vector::const_iterator bu = cells_bu.begin (); bu != cells_bu.end (); ++bu) { - if (later.find (*bu) == later.end ()) { + LocalProcessorContexts::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu)); + if (cpc != contexts.context_map ().end ()) { + + if (later.find (*bu) == later.end ()) { - LocalProcessorContexts::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu)); - if (cpc != contexts.context_map ().end ()) { rc_job->schedule (new LocalProcessorResultComputationTask (this, contexts, cpc->first, &cpc->second, op, output_layer)); any = true; - for (db::Cell::parent_cell_iterator pc = cpc->first->begin_parent_cells (); pc != cpc->first->end_parent_cells (); ++pc) { - later.insert (*pc); - } + + } else { + next_cells_bu.push_back (*bu); + } + + for (db::Cell::parent_cell_iterator pc = cpc->first->begin_parent_cells (); pc != cpc->first->end_parent_cells (); ++pc) { + later.insert (*pc); } - } else { - next_cells_bu.push_back (*bu); } } diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index 4cba12d1b..5ed1ecb4e 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -103,6 +103,7 @@ class DB_PUBLIC LocalProcessorCellContext { public: typedef std::pair parent_inst_type; + typedef std::vector::const_iterator drop_iterator; LocalProcessorCellContext (); LocalProcessorCellContext (const LocalProcessorCellContext &other); @@ -130,6 +131,18 @@ public: return m_lock; } + // used for debugging purposes only + drop_iterator begin_drops () const + { + return m_drops.begin (); + } + + // used for debugging purposes only + drop_iterator end_drops () const + { + return m_drops.end (); + } + private: std::unordered_set m_propagated; std::vector m_drops; diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index 79f344f94..a82fa11ff 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -109,6 +109,11 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vectorbox().to_string().c_str()); + printf("@@@ rdiff=%s\n", rdiff.to_string().c_str()); + printf("@@@ rgates=%s\n", rgates.to_string().c_str()); fflush(stdout); + } tl_assert (n > 0); device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, dbu () * dbu () * d->area () / double (n)); From 458d00969ce77259587ca9c894d855e96ca19764 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Jan 2019 23:59:22 +0100 Subject: [PATCH 218/335] Fixed issue #228 Reason for the problem: the interaction test has to keep separate "inside" records for both below and above the scanline, not just once. With the single record, the step in the left polygon erased the "inside" condition. --- src/db/db/dbEdgeProcessor.cc | 28 ++++++++++++++++++++-------- src/db/db/dbEdgeProcessor.h | 4 ++-- src/db/unit_tests/dbRegion.cc | 24 ++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 0a1481cd1..756f6af72 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -639,7 +639,8 @@ InteractionDetector::reset () { m_wcv_n.clear (); m_wcv_s.clear (); - m_inside.clear (); + m_inside_n.clear (); + m_inside_s.clear (); } void @@ -649,7 +650,8 @@ InteractionDetector::reserve (size_t n) m_wcv_s.clear (); m_wcv_n.resize (n, 0); m_wcv_s.resize (n, 0); - m_inside.clear (); + m_inside_n.clear (); + m_inside_s.clear (); } int @@ -667,9 +669,11 @@ InteractionDetector::edge (bool north, bool enter, property_type p) // we have to catch interactions between objects north and south to the scanline if (north || (m_mode == 0 && m_include_touching)) { + std::set *inside = north ? &m_inside_n : &m_inside_s; + if (inside_after < inside_before) { - m_inside.erase (p); + inside->erase (p); if (m_mode != 0) { @@ -677,7 +681,7 @@ InteractionDetector::edge (bool north, bool enter, property_type p) // (due to prefer_touch == true and the sorting of coincident edges by property id) // hence every remaining parts count as non-interacting (outside) if (p == m_container_id) { - for (std::set ::const_iterator i = m_inside.begin (); i != m_inside.end (); ++i) { + for (std::set ::const_iterator i = inside->begin (); i != inside->end (); ++i) { if (*i != m_container_id) { m_non_interactions.insert (*i); } @@ -695,13 +699,13 @@ InteractionDetector::edge (bool north, bool enter, property_type p) // note that the container parts will be delivered first of all coincident // edges hence we can check whether the container is present even for coincident // edges - if (m_inside.find (m_container_id) != m_inside.end ()) { + if (inside->find (m_container_id) != inside->end ()) { m_interactions.insert (std::make_pair (m_container_id, p)); } else { m_non_interactions.insert (p); } } else { - for (std::set ::const_iterator i = m_inside.begin (); i != m_inside.end (); ++i) { + for (std::set ::const_iterator i = inside->begin (); i != inside->end (); ++i) { if (*i != m_container_id) { m_interactions.insert (std::make_pair (m_container_id, *i)); } @@ -710,7 +714,15 @@ InteractionDetector::edge (bool north, bool enter, property_type p) } else { - for (std::set ::const_iterator i = m_inside.begin (); i != m_inside.end (); ++i) { + for (std::set ::const_iterator i = m_inside_n.begin (); i != m_inside_n.end (); ++i) { + if (*i < p) { + m_interactions.insert (std::make_pair (*i, p)); + } else if (*i > p) { + m_interactions.insert (std::make_pair (p, *i)); + } + } + + for (std::set ::const_iterator i = m_inside_s.begin (); i != m_inside_s.end (); ++i) { if (*i < p) { m_interactions.insert (std::make_pair (*i, p)); } else if (*i > p) { @@ -720,7 +732,7 @@ InteractionDetector::edge (bool north, bool enter, property_type p) } - m_inside.insert (p); + inside->insert (p); } diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index bca103bc2..9cca1308a 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -299,7 +299,7 @@ public: virtual void reserve (size_t n); virtual int edge (bool north, bool enter, property_type p); virtual int compare_ns () const; - virtual bool is_reset () const { return m_inside.empty (); } + virtual bool is_reset () const { return m_inside_s.empty () && m_inside_n.empty (); } virtual bool prefer_touch () const { return m_include_touching; } private: @@ -307,7 +307,7 @@ private: bool m_include_touching; property_type m_container_id; std::vector m_wcv_n, m_wcv_s; - std::set m_inside; + std::set m_inside_n, m_inside_s; std::set > m_interactions; std::set m_non_interactions; }; diff --git a/src/db/unit_tests/dbRegion.cc b/src/db/unit_tests/dbRegion.cc index 6be478378..954a09831 100644 --- a/src/db/unit_tests/dbRegion.cc +++ b/src/db/unit_tests/dbRegion.cc @@ -1338,3 +1338,27 @@ TEST(30c) EXPECT_EQ (r.to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); } +TEST(issue_228) +{ + db::Region r; + db::Point pts[] = { + db::Point (0, 10), + db::Point (0, 290), + db::Point (280, 290), + db::Point (280, 230), + db::Point (360, 230), + db::Point (360, 70), + db::Point (280,70), + db::Point (280,10) + }; + + db::Polygon poly; + poly.assign_hull (pts, pts + sizeof (pts) / sizeof (pts [0])); + r.insert (poly); + + db::Region rr; + rr.insert (db::Box (360, 70, 480, 230)); + + EXPECT_EQ (r.selected_interacting (rr).to_string (), r.to_string ()); + EXPECT_EQ (rr.selected_interacting (r).to_string (), rr.to_string ()); +} From 794c31329a5a45332e0888ef73eb26e5b5a69e40 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Jan 2019 22:00:30 +0100 Subject: [PATCH 219/335] Bugfix: internal error when running netlist extraction. --- src/db/db/dbHierNetworkProcessor.cc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 8ad42cd7d..f9a9f4d2e 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1388,6 +1388,11 @@ private: */ void mark_to_join (id_type a, id_type b) { + if (a == b) { + // shouldn't happen, but duplicate instances may trigger this + return; + } + typename std::map::const_iterator x = m_cm2join_map.find (a); typename std::map::const_iterator y = m_cm2join_map.find (b); @@ -1885,12 +1890,14 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c db::local_cluster *gc = local.insert (); gc->set_global_nets (ge->first); + // NOTE: don't use the gc pointer - it may become invalid during make_path (will also do a local.insert) + size_t gcid = gc->id (); for (std::set::const_iterator ci = ge->second.begin (); ci != ge->second.end (); ++ci) { if (ci->inst ().array_inst.at_end ()) { - local.join_cluster_with (gc->id (), ci->id ()); + local.join_cluster_with (gcid, ci->id ()); local.remove_cluster (ci->id ()); } else { @@ -1900,11 +1907,13 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c ClusterInstance k = make_path (layout, cell, ci->id (), p); size_t other_id = local.find_cluster_with_connection (k); - if (other_id) { - local.join_cluster_with (gc->id (), other_id); + if (other_id == gcid) { + // shouldn't happen, but duplicate instances may trigger this + } else if (other_id) { + local.join_cluster_with (gcid, other_id); local.remove_cluster (other_id); } else { - local.add_connection (gc->id (), k); + local.add_connection (gcid, k); } } From 2da7b218b48c71b20fc42fc54c421ce96a301597 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Jan 2019 22:01:29 +0100 Subject: [PATCH 220/335] Print errors on log when running device extractor. --- src/db/db/dbNetlistDeviceExtractor.cc | 50 +++++++++++++++++++++++++-- src/db/db/dbNetlistDeviceExtractor.h | 5 +++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index a0d40cb31..3edbf16bd 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -27,6 +27,7 @@ #include "tlProgress.h" #include "tlTimer.h" +#include "tlInternational.h" namespace db { @@ -45,6 +46,31 @@ NetlistDeviceExtractorError::NetlistDeviceExtractorError (const std::string &cel // .. nothing yet .. } +std::string NetlistDeviceExtractorError::to_string () const +{ + std::string res; + + if (! m_category_name.empty ()) { + if (m_category_description.empty ()) { + res += "[" + m_category_name + "] "; + } else { + res += "[" + m_category_description + "] "; + } + } + + res += m_message; + + if (! m_cell_name.empty ()) { + res += tl::to_string (tr (", in cell: ")) + m_cell_name; + } + + if (! m_geometry.box ().empty ()) { + res += tl::to_string (tr (", shape: ")) + m_geometry.to_string (); + } + + return res; +} + // ---------------------------------------------------------------------------------------- // NetlistDeviceExtractor implementation @@ -485,25 +511,43 @@ std::string NetlistDeviceExtractor::cell_name () const void NetlistDeviceExtractor::error (const std::string &msg) { m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); + + if (tl::verbosity () >= 20) { + tl::error << m_errors.back ().to_string (); + } } void NetlistDeviceExtractor::error (const std::string &msg, const db::DPolygon &poly) { - error (msg); + m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); m_errors.back ().set_geometry (poly); + + if (tl::verbosity () >= 20) { + tl::error << m_errors.back ().to_string (); + } } void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg) { - error (msg); + m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); m_errors.back ().set_category_name (category_name); m_errors.back ().set_category_description (category_description); + + if (tl::verbosity () >= 20) { + tl::error << m_errors.back ().to_string (); + } } void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::DPolygon &poly) { - error (category_name, category_description, msg); + m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); + m_errors.back ().set_category_name (category_name); + m_errors.back ().set_category_description (category_description); m_errors.back ().set_geometry (poly); + + if (tl::verbosity () >= 20) { + tl::error << m_errors.back ().to_string (); + } } } diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index cbd8ec85a..c82e189d4 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -138,6 +138,11 @@ public: m_cell_name = n; } + /** + * @brief Formats this message for printing + */ + std::string to_string () const; + private: std::string m_cell_name; std::string m_message; From da8b2854de58478f39060e8ef8e19fb1572c28c7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 28 Jan 2019 21:27:41 +0100 Subject: [PATCH 221/335] Some fine-tuning of the timer output verbosity of the edge processor. --- src/db/db/dbEdgeProcessor.cc | 12 +++++++++--- src/db/db/dbEdgeProcessor.h | 9 +++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 756f6af72..2447a47b8 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -975,7 +975,7 @@ BooleanOp2::compare_ns () const // EdgeProcessor implementation EdgeProcessor::EdgeProcessor (bool report_progress, const std::string &progress_desc) - : m_report_progress (report_progress), m_progress_desc (progress_desc) + : m_report_progress (report_progress), m_progress_desc (progress_desc), m_base_verbosity (30) { mp_work_edges = new std::vector (); mp_cpvector = new std::vector (); @@ -1006,6 +1006,12 @@ EdgeProcessor::enable_progress (const std::string &progress_desc) m_progress_desc = progress_desc; } +void +EdgeProcessor::set_base_verbosity (int bv) +{ + m_base_verbosity = bv; +} + void EdgeProcessor::reserve (size_t n) { @@ -1534,7 +1540,7 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector void EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) { - tl::SelfTimer timer (tl::verbosity () >= 31, "EdgeProcessor: process"); + tl::SelfTimer timer (tl::verbosity () >= m_base_verbosity, "EdgeProcessor: process"); bool prefer_touch = op.prefer_touch (); bool selects_edges = op.selects_edges (); @@ -1749,7 +1755,7 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) #endif - tl::SelfTimer timer2 (tl::verbosity () >= 41, "EdgeProcessor: production"); + tl::SelfTimer timer2 (tl::verbosity () >= m_base_verbosity + 10, "EdgeProcessor: production"); // step 4: compute the result edges diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index 9cca1308a..a3593499a 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -606,6 +606,14 @@ public: */ void disable_progress (); + /** + * @brief Base verbosity for timer reporting + * + * The default value is 30. Basic timing will be reported for > base_verbosity, detailed timing + * for > base_verbosity + 10. + */ + void set_base_verbosity (int bv); + /** * @brief Reserve space for at least n edges */ @@ -943,6 +951,7 @@ private: std::vector *mp_cpvector; bool m_report_progress; std::string m_progress_desc; + int m_base_verbosity; static size_t count_edges (const db::Polygon &q) { From 7e42ff83cd80ebe2c0d8d3d2b36f54c654404a66 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 28 Jan 2019 21:28:23 +0100 Subject: [PATCH 222/335] More fine-tuning of verbosity of log output for local processor. --- src/db/db/dbHierProcessor.cc | 11 ++++++----- src/db/db/dbLocalOperation.cc | 2 ++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index c84236f9d..a2f4d83d6 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -262,6 +262,7 @@ subtract (std::unordered_set &res, const std::unordered_set= 30) { + if (tl::verbosity () >= 50) { tl::log << tr ("Computing local results for ") << cell->layout ()->cell_name (cell->cell_index ()) << " (context " << index << "/" << total << ")"; } @@ -836,7 +837,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const L { try { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing contexts for ")) + description (op)); + tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Computing contexts for ")) + description (op)); if (m_nthreads > 0) { mp_cc_job.reset (new tl::Job (m_nthreads)); @@ -891,7 +892,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, { CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts) - if (tl::verbosity () >= 30) { + if (tl::verbosity () >= 50) { if (! subject_parent) { tl::log << tr ("Computing context for top cell ") << mp_subject_layout->cell_name (subject_cell->cell_index ()); } else { @@ -1089,7 +1090,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, void LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int output_layer) const { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing results for ")) + description (op)); + tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Computing results for ")) + description (op)); // avoids updates while we work on the layout mp_subject_layout->update (); @@ -1113,7 +1114,7 @@ LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOp while (true) { ++iter; - tl::SelfTimer timer (tl::verbosity () >= 21, tl::sprintf (tl::to_string (tr ("Computing results iteration #%d")), iter)); + tl::SelfTimer timer (tl::verbosity () >= 41, tl::sprintf (tl::to_string (tr ("Computing results iteration #%d")), iter)); bool any = false; std::unordered_set later; diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index 665b1817c..739d0fab4 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -105,6 +105,7 @@ BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const ShapeIntera db::PolygonRefGenerator pr (layout, result); db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); db::PolygonGenerator pg (splitter, true, true); + ep.set_base_verbosity (50); ep.process (pg, op); } @@ -158,6 +159,7 @@ void SelfOverlapMergeLocalOperation::compute_local (db::Layout *layout, const Sh db::MergeOp op (m_wrap_count - 1); db::PolygonRefGenerator pr (layout, result); db::PolygonGenerator pg (pr, true, true); + ep.set_base_verbosity (50); ep.process (pg, op); } From 7d06ea83c19f036979288ec57689fa1afb254031 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 28 Jan 2019 21:29:34 +0100 Subject: [PATCH 223/335] Bugfix: a segfault happened during net cluster formation because make_path was messing up the memory layout of the cluster collections. --- src/db/db/dbHierNetworkProcessor.cc | 141 ++++++++++++++++------------ src/db/db/dbHierNetworkProcessor.h | 6 +- 2 files changed, 88 insertions(+), 59 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index f9a9f4d2e..d33a5631f 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1004,6 +1004,17 @@ public: typedef typename local_cluster::id_type id_type; typedef std::map connector_map; + struct ClusterInstanceInteraction + { + ClusterInstanceInteraction (size_t _cluster_id, size_t _other_cluster_id, const std::vector &_other_path) + : cluster_id (_cluster_id), other_cluster_id (_other_cluster_id), other_path (_other_path) + { } + + size_t cluster_id; + size_t other_cluster_id; + std::vector other_path; + }; + /** * @brief Constructor */ @@ -1054,8 +1065,26 @@ public: * This step is postponed because doing this while the iteration happens would * invalidate the box trees. */ - void join_superclusters () + void finish_cluster_to_instance_interactions () { + for (typename std::list::const_iterator ii = m_ci_interactions.begin (); ii != m_ci_interactions.end (); ++ii) { + + ClusterInstance other_key = make_path (ii->other_cluster_id, ii->other_path); + + id_type other = mp_cell_clusters->find_cluster_with_connection (other_key); + if (other > 0) { + + // we found a child cluster that connects two clusters on our own level: + // we must join them into one, but not now. We're still iterating and + // would invalidate the box trees. So keep this now and combine the clusters later. + mark_to_join (other, ii->cluster_id); + + } else { + mp_cell_clusters->add_connection (ii->cluster_id, other_key); + } + + } + for (typename std::list >::const_iterator sc = m_cm2join_sets.begin (); sc != m_cm2join_sets.end (); ++sc) { if (sc->empty ()) { @@ -1082,6 +1111,7 @@ private: typedef std::list > join_set_list; std::map m_cm2join_map; join_set_list m_cm2join_sets; + std::list m_ci_interactions; /** * @brief Handles the cluster interactions between two instances or instance arrays @@ -1184,6 +1214,10 @@ private: db::ICplxTrans t2i = t2.inverted (); db::ICplxTrans t21 = t1i * t2; + // NOTE: make_path may disturb the iteration (because of modification), hence + // we first collect and then process the interactions. + std::vector > interactions; + for (typename db::local_clusters::touching_iterator i = cl1.begin_touching (common.transformed (t1i)); ! i.at_end (); ++i) { // skip the test, if this cluster doesn't interact with the whole cell2 @@ -1195,48 +1229,51 @@ private: for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (bc1.transformed (t2i)); ! j.at_end (); ++j) { if (i->interacts (*j, t21, *mp_conn)) { - - ClusterInstance k1 = make_path (i->id (), p1); - ClusterInstance k2 = make_path (j->id (), p2); - - id_type x1 = mp_cell_clusters->find_cluster_with_connection (k1); - id_type x2 = mp_cell_clusters->find_cluster_with_connection (k2); - - if (x1 == 0) { - - if (x2 == 0) { - - id_type connector = mp_cell_clusters->insert_dummy (); - mp_cell_clusters->add_connection (connector, k1); - mp_cell_clusters->add_connection (connector, k2); - - } else { - mp_cell_clusters->add_connection (x2, k1); - } - - } else if (x2 == 0) { - - mp_cell_clusters->add_connection (x1, k2); - - } else if (x1 != x2) { - - // for instance-to-instance interactions the number of connections is more important for the - // cost of the join operation: make the one with more connections the target - if (mp_cell_clusters->connections_for_cluster (x1).size () < mp_cell_clusters->connections_for_cluster (x2).size ()) { - std::swap (x1, x2); - } - - mp_cell_clusters->join_cluster_with (x1, x2); - mp_cell_clusters->remove_cluster (x2); - - } - + interactions.push_back (std::make_pair (i->id (), j->id ())); } } } + for (std::vector >::const_iterator ii = interactions.begin (); ii != interactions.end (); ++ii) { + + ClusterInstance k1 = make_path (ii->first, p1); + ClusterInstance k2 = make_path (ii->second, p2); + + id_type x1 = mp_cell_clusters->find_cluster_with_connection (k1); + id_type x2 = mp_cell_clusters->find_cluster_with_connection (k2); + + if (x1 == 0) { + + if (x2 == 0) { + + id_type connector = mp_cell_clusters->insert_dummy (); + mp_cell_clusters->add_connection (connector, k1); + mp_cell_clusters->add_connection (connector, k2); + + } else { + mp_cell_clusters->add_connection (x2, k1); + } + + } else if (x2 == 0) { + + mp_cell_clusters->add_connection (x1, k2); + + } else if (x1 != x2) { + + // for instance-to-instance interactions the number of connections is more important for the + // cost of the join operation: make the one with more connections the target + if (mp_cell_clusters->connections_for_cluster (x1).size () < mp_cell_clusters->connections_for_cluster (x2).size ()) { + std::swap (x1, x2); + } + + mp_cell_clusters->join_cluster_with (x1, x2); + mp_cell_clusters->remove_cluster (x2); + + } + + } } /** @@ -1358,26 +1395,15 @@ private: void add_single_pair (const local_cluster &c1, db::cell_index_type ci2, const std::vector &p2, const db::ICplxTrans &t2) { + // NOTE: make_path may disturb the iteration (because of modification), hence + // we first collect and then process the interactions. + const db::local_clusters &cl2 = mp_tree->clusters_per_cell (ci2); for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (c1.bbox ().transformed (t2.inverted ())); ! j.at_end (); ++j) { if (c1.interacts (*j, t2, *mp_conn)) { - - ClusterInstance k2 = make_path (j->id (), p2); - - id_type other = mp_cell_clusters->find_cluster_with_connection (k2); - if (other > 0) { - - // we found a child cluster that connects two clusters on our own level: - // we must join them into one, but not now. We're still iterating and - // would invalidate the box trees. So keep this now and combine the clusters later. - mark_to_join (other, c1.id ()); - - } else { - mp_cell_clusters->add_connection (c1.id (), k2); - } - + m_ci_interactions.push_back (ClusterInstanceInteraction (c1.id (), j->id (), p2)); } } @@ -1649,19 +1675,17 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou todo.push_back (*c); } else { tl_assert (! todo.empty ()); - build_hier_connections_for_cells (cbc, layout, todo, conn); + build_hier_connections_for_cells (cbc, layout, todo, conn, progress); done.insert (todo.begin (), todo.end ()); todo.clear (); todo.push_back (*c); } - ++progress; - } } - build_hier_connections_for_cells (cbc, layout, todo, conn); + build_hier_connections_for_cells (cbc, layout, todo, conn, progress); } } @@ -1681,10 +1705,11 @@ hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell template void -hier_clusters::build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn) +hier_clusters::build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn, tl::RelativeProgress &progress) { for (std::vector::const_iterator c = cells.begin (); c != cells.end (); ++c) { build_hier_connections (cbc, layout, layout.cell (*c), conn); + ++progress; } } @@ -1850,7 +1875,7 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c } // join local clusters which got connected by child clusters - rec->join_superclusters (); + rec->finish_cluster_to_instance_interactions (); rec.reset (0); // finally connect global nets diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 535d18752..d34d0b7dd 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -34,6 +34,10 @@ #include #include +namespace tl { + class RelativeProgress; +} + namespace db { class DeepLayer; @@ -791,7 +795,7 @@ public: private: void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); void build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn); - void build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn); + void build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn, tl::RelativeProgress &progress); void do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); std::map > m_per_cell_clusters; From 4068478887262abc55dafe3d7621bc0a12575b8d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 31 Jan 2019 00:07:10 +0100 Subject: [PATCH 224/335] Implemented SPICE writer + tests. --- src/db/db/db.pro | 8 +- src/db/db/dbNetlistSpiceWriter.cc | 402 ++++++++++ src/db/db/dbNetlistSpiceWriter.h | 111 +++ src/db/db/dbNetlistWriter.cc | 31 + src/db/db/dbNetlistWriter.h | 90 +++ src/db/db/gsiDeclDbNetlist.cc | 241 +++++- src/db/unit_tests/dbNetlistTests.cc | 1 + src/db/unit_tests/dbNetlistWriterTests.cc | 919 ++++++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 3 +- src/rba/unit_tests/rba.cc | 1 + testdata/algo/nwriter10_au.txt | 413 ++++++++++ testdata/algo/nwriter1_au.txt | 14 + testdata/algo/nwriter20_au.txt | 25 + testdata/algo/nwriter2_au.txt | 14 + testdata/algo/nwriter3_au.txt | 14 + testdata/algo/nwriter4_au.txt | 14 + testdata/algo/nwriter5_au.txt | 16 + testdata/algo/nwriter6_au.txt | 18 + testdata/algo/nwriter7_au.txt | 14 + testdata/algo/nwriter8_au.txt | 34 + testdata/algo/nwriter_rba1_au.txt | 13 + testdata/algo/nwriter_rba2_au.txt | 29 + testdata/ruby/dbNetlistWriterTests.rb | 127 +++ 23 files changed, 2541 insertions(+), 11 deletions(-) create mode 100644 src/db/db/dbNetlistSpiceWriter.cc create mode 100644 src/db/db/dbNetlistSpiceWriter.h create mode 100644 src/db/db/dbNetlistWriter.cc create mode 100644 src/db/db/dbNetlistWriter.h create mode 100644 src/db/unit_tests/dbNetlistWriterTests.cc create mode 100644 testdata/algo/nwriter10_au.txt create mode 100644 testdata/algo/nwriter1_au.txt create mode 100644 testdata/algo/nwriter20_au.txt create mode 100644 testdata/algo/nwriter2_au.txt create mode 100644 testdata/algo/nwriter3_au.txt create mode 100644 testdata/algo/nwriter4_au.txt create mode 100644 testdata/algo/nwriter5_au.txt create mode 100644 testdata/algo/nwriter6_au.txt create mode 100644 testdata/algo/nwriter7_au.txt create mode 100644 testdata/algo/nwriter8_au.txt create mode 100644 testdata/algo/nwriter_rba1_au.txt create mode 100644 testdata/algo/nwriter_rba2_au.txt create mode 100644 testdata/ruby/dbNetlistWriterTests.rb diff --git a/src/db/db/db.pro b/src/db/db/db.pro index ef6d26aa5..53e212822 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -164,7 +164,9 @@ SOURCES = \ dbLayoutToNetlistFormatDefs.cc \ dbDeviceAbstract.cc \ dbLocalOperationUtils.cc \ - gsiDeclDbDeepShapeStore.cc + gsiDeclDbDeepShapeStore.cc \ + dbNetlistSpiceWriter.cc \ + dbNetlistWriter.cc HEADERS = \ dbArray.h \ @@ -293,7 +295,9 @@ HEADERS = \ dbLayoutToNetlistFormatDefs.h \ dbDeviceAbstract.h \ dbLocalOperationUtils.h \ - dbDeepRegion.h + dbDeepRegion.h \ + dbNetlistSpiceWriter.h \ + dbNetlistWriter.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc new file mode 100644 index 000000000..338edce2b --- /dev/null +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -0,0 +1,402 @@ + +/* + + 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 "dbNetlistSpiceWriter.h" +#include "dbNetlist.h" +#include "dbNetlistDeviceClasses.h" + +#include "tlStream.h" + +#include + +namespace db +{ + +// -------------------------------------------------------------------------------- + +NetlistSpiceWriterDelegate::NetlistSpiceWriterDelegate () + : mp_writer (0) +{ + // .. nothing yet .. +} + +NetlistSpiceWriterDelegate::~NetlistSpiceWriterDelegate () +{ + // .. nothing yet .. +} + +std::string NetlistSpiceWriterDelegate::net_to_string (const db::Net *net) const +{ + tl_assert (mp_writer != 0); + return mp_writer->net_to_string (net); +} + +std::string NetlistSpiceWriterDelegate::format_name (const std::string &name) const +{ + tl_assert (mp_writer != 0); + return mp_writer->format_name (name); +} + +void NetlistSpiceWriterDelegate::emit_line (const std::string &line) const +{ + tl_assert (mp_writer != 0); + mp_writer->emit_line (line); +} + +void NetlistSpiceWriterDelegate::emit_comment (const std::string &comment) const +{ + tl_assert (mp_writer != 0); + mp_writer->emit_comment (comment); +} + +void NetlistSpiceWriterDelegate::attach_writer (NetlistSpiceWriter *writer) +{ + mp_writer = writer; +} + +void NetlistSpiceWriterDelegate::write_header () const +{ + // .. nothing yet .. +} + +void NetlistSpiceWriterDelegate::write_device_intro (const db::DeviceClass &) const +{ + // .. nothing yet .. +} + +void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const +{ + const db::DeviceClass *dc = dev.device_class (); + const db::DeviceClassCapacitor *cap = dynamic_cast (dc); + const db::DeviceClassInductor *ind = dynamic_cast (dc); + const db::DeviceClassResistor *res = dynamic_cast (dc); + const db::DeviceClassDiode *diode = dynamic_cast (dc); + const db::DeviceClassMOS3Transistor *mos3 = dynamic_cast (dc); + const db::DeviceClassMOS4Transistor *mos4 = dynamic_cast (dc); + + std::ostringstream os; + + if (cap) { + + os << "C"; + os << format_name (dev.expanded_name ()); + os << format_terminals (dev); + os << " "; + os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassCapacitor::param_id_C)); + + } else if (ind) { + + os << "L"; + os << format_name (dev.expanded_name ()); + os << format_terminals (dev); + os << " "; + os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassInductor::param_id_L)); + + } else if (res) { + + os << "R"; + os << format_name (dev.expanded_name ()); + os << format_terminals (dev); + os << " "; + os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassResistor::param_id_R)); + + } else if (diode) { + + os << "D"; + os << format_name (dev.expanded_name ()); + os << format_terminals (dev); + + // Use "D" + device class name for the model + os << " D"; + os << format_name (dev.device_class ()->name ()); + + } else if (mos3 || mos4) { + + os << "M"; + os << format_name (dev.expanded_name ()); + os << format_terminals (dev); + + if (mos3) { + // we assume for the MOS3 type the bulk is connected to Source + os << " "; + os << net_to_string (dev.net_for_terminal (db::DeviceClassMOS3Transistor::terminal_id_S)); + } + + // Use "M" + device class name for the model + os << " M"; + os << format_name (dev.device_class ()->name ()); + + os << " L=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_L)); + os << " W=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_W)); + os << " AS=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AS)); + os << " AD=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AD)); + + } else { + + // Write unknown devices as subcircuits (CAUTION: potential name clash) + os << "XD_" << format_name (dev.expanded_name ()); + os << format_terminals (dev); + os << " "; + os << format_name (dev.device_class ()->name ()); + os << " PARAMS:"; + os << format_params (dev); + + } + + emit_line (os.str ()); +} + +std::string NetlistSpiceWriterDelegate::format_terminals (const db::Device &dev) const +{ + std::ostringstream os; + + const std::vector &td = dev.device_class ()->terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + os << " " << net_to_string (dev.net_for_terminal (i->id ())); + } + + return os.str (); +} + +std::string NetlistSpiceWriterDelegate::format_params (const db::Device &dev) const +{ + std::ostringstream os; + + const std::vector &pd = dev.device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + os << " " << i->name () << "=" << tl::to_string (dev.parameter_value (i->id ())); + } + + return os.str (); +} + +// -------------------------------------------------------------------------------- + +NetlistSpiceWriter::NetlistSpiceWriter (NetlistSpiceWriterDelegate *delegate) + : mp_netlist (0), mp_stream (0), mp_delegate (delegate) +{ + static NetlistSpiceWriterDelegate std_delegate; + if (! delegate) { + mp_delegate.reset (&std_delegate); + } +} + +NetlistSpiceWriter::~NetlistSpiceWriter () +{ + // .. nothing yet .. +} + +void NetlistSpiceWriter::write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description) +{ + mp_stream = &stream; + mp_netlist = &netlist; + mp_delegate->attach_writer (this); + + try { + + do_write (description); + + mp_stream = 0; + mp_netlist = 0; + mp_delegate->attach_writer (0); + + } catch (...) { + + mp_stream = 0; + mp_netlist = 0; + mp_delegate->attach_writer (0); + throw; + + } +} + +std::string NetlistSpiceWriter::net_to_string (const db::Net *net) const +{ + std::map::const_iterator n = m_net_to_spice_id.find (net); + if (! net || n == m_net_to_spice_id.end ()) { + // TODO: this should assert or similar + return "0"; + } else { + return tl::to_string (n->second); + } +} + +void NetlistSpiceWriter::emit_line (const std::string &line) const +{ + tl_assert (mp_stream != 0); + + int max_length = 80; + bool first = true; + + const char *cp = line.c_str (); + do { + + const char *cpn = cp; + const char *cspc = 0; + int c = 0; + + int l = first ? max_length : max_length - 2; + while (*cpn && (c < l || ! cspc)) { + if (isspace (*cpn)) { + cspc = cpn; + } + ++c; + ++cpn; + } + + if (! first) { + *mp_stream << "+ "; + } + + if (! *cpn) { + *mp_stream << cp << "\n"; + break; + } else { + while (*cp && (cp != cspc || ! cspc)) { + *mp_stream << *cp++; + } + *mp_stream << "\n"; + } + + first = false; + + while (*cp && isspace (*cp)) { + ++cp; + } + + } while (*cp); +} + +void NetlistSpiceWriter::emit_comment (const std::string &comment) const +{ + tl_assert (mp_stream != 0); + + // TODO: should do some line breaking or reduction for long lines + // or when lines contain newlines + *mp_stream << "* " << comment << "\n"; +} + +std::string NetlistSpiceWriter::format_name (const std::string &s) const +{ + // TODO: escape or replace special chars + return s; +} + +void NetlistSpiceWriter::do_write (const std::string &description) +{ + if (! description.empty ()) { + emit_comment (description); + } + + mp_delegate->write_header (); + + for (db::Netlist::const_device_class_iterator dc = mp_netlist->begin_device_classes (); dc != mp_netlist->end_device_classes (); ++dc) { + mp_delegate->write_device_intro (*dc); + } + + for (db::Netlist::const_top_down_circuit_iterator c = mp_netlist->begin_top_down (); c != mp_netlist->end_top_down (); ++c) { + + const db::Circuit &circuit = **c; + + // assign internal node numbers to the nets + m_net_to_spice_id.clear (); + size_t nid = 0; + for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + m_net_to_spice_id.insert (std::make_pair (n.operator-> (), ++nid)); + } + + write_circuit_header (circuit); + + for (db::Circuit::const_subcircuit_iterator i = circuit.begin_subcircuits (); i != circuit.end_subcircuits (); ++i) { + write_subcircuit_call (*i); + } + + for (db::Circuit::const_device_iterator i = circuit.begin_devices (); i != circuit.end_devices (); ++i) { + + // TODO: make this configurable? + std::string comment = "device instance " + i->expanded_name () + " " + i->position ().to_string () + " " + i->device_class ()->name (); + emit_comment (comment); + + mp_delegate->write_device (*i); + + } + + write_circuit_end (circuit); + + } +} + +void NetlistSpiceWriter::write_subcircuit_call (const db::SubCircuit &subcircuit) const +{ + // TODO: make this configurable? + std::string comment = "cell instance " + subcircuit.expanded_name() + " " + subcircuit.trans ().to_string (); + emit_comment (comment); + + std::ostringstream os; + os << "X"; + os << format_name (subcircuit.expanded_name ()); + + for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) { + os << " "; + os << net_to_string (subcircuit.net_for_pin (p->id ())); + } + + os << " "; + os << format_name (subcircuit.circuit_ref ()->name ()); + + emit_line (os.str ()); +} + +void NetlistSpiceWriter::write_circuit_header (const db::Circuit &circuit) const +{ + emit_line (""); + + emit_comment ("cell " + circuit.name ()); + for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { + emit_comment ("pin " + p->name ()); + } + + std::ostringstream os; + + os << ".SUBCKT "; + os << format_name (circuit.name ()); + + for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { + os << " "; + os << net_to_string (circuit.net_for_pin (p->id ())); + } + + emit_line (os.str ()); + + for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + if (! n->name ().empty ()) { + emit_comment ("net " + net_to_string (n.operator-> ()) + " " + n->name ()); + } + } +} + +void NetlistSpiceWriter::write_circuit_end (const db::Circuit &circuit) const +{ + emit_line (".ENDS " + format_name (circuit.name ())); +} + +} diff --git a/src/db/db/dbNetlistSpiceWriter.h b/src/db/db/dbNetlistSpiceWriter.h new file mode 100644 index 000000000..d8dd294d3 --- /dev/null +++ b/src/db/db/dbNetlistSpiceWriter.h @@ -0,0 +1,111 @@ + +/* + + 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_dbNetlistSpiceWriter +#define HDR_dbNetlistSpiceWriter + +#include "dbCommon.h" +#include "dbNetlistWriter.h" +#include "tlObject.h" + +#include +#include + +namespace db +{ + +class DeviceClass; +class Device; +class Net; +class NetlistSpiceWriter; +class Circuit; +class SubCircuit; + +/** + * @brief A device writer delegate for the SPICE writer + * + * This delegate is supposed to provide the mapping of devices to parametrized SPICE subcircuits. + * It is generic, so it can be used for other cases of device mapping. + */ +class DB_PUBLIC NetlistSpiceWriterDelegate + : public tl::Object +{ +public: + NetlistSpiceWriterDelegate (); + virtual ~NetlistSpiceWriterDelegate (); + + virtual void write_header () const; + virtual void write_device_intro (const db::DeviceClass &cls) const; + virtual void write_device (const db::Device &dev) const; + +protected: + std::string net_to_string (const db::Net *net) const; + void emit_line (const std::string &line) const; + void emit_comment (const std::string &comment) const; + std::string format_name (const std::string &s) const; + std::string format_terminals (const db::Device &dev) const; + std::string format_params (const db::Device &dev) const; + +private: + friend class NetlistSpiceWriter; + + NetlistSpiceWriter *mp_writer; + + void attach_writer (NetlistSpiceWriter *writer); +}; + +/** + * @brief A SPICE format writer for netlists + * + * Specialization happens through the device writer delegate. + */ +class DB_PUBLIC NetlistSpiceWriter + : public NetlistWriter +{ +public: + NetlistSpiceWriter (NetlistSpiceWriterDelegate *delegate = 0); + virtual ~NetlistSpiceWriter (); + + virtual void write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description); + +private: + friend class NetlistSpiceWriterDelegate; + + const db::Netlist *mp_netlist; + tl::OutputStream *mp_stream; + tl::weak_ptr mp_delegate; + std::map m_net_to_spice_id; + + void do_write (const std::string &description); + + std::string net_to_string (const db::Net *net) const; + void emit_line (const std::string &line) const; + void emit_comment (const std::string &comment) const; + std::string format_name (const std::string &name) const; + void write_subcircuit_call (const db::SubCircuit &subcircuit) const; + void write_circuit_header (const db::Circuit &circuit) const; + void write_circuit_end (const db::Circuit &circuit) const; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistWriter.cc b/src/db/db/dbNetlistWriter.cc new file mode 100644 index 000000000..2c40a9c78 --- /dev/null +++ b/src/db/db/dbNetlistWriter.cc @@ -0,0 +1,31 @@ + +/* + + 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 "dbNetlistWriter.h" + +namespace db +{ + + // .. nothing yet .. + +} diff --git a/src/db/db/dbNetlistWriter.h b/src/db/db/dbNetlistWriter.h new file mode 100644 index 000000000..2324f7454 --- /dev/null +++ b/src/db/db/dbNetlistWriter.h @@ -0,0 +1,90 @@ + +/* + + 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 + +*/ + + +/* + + 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_dbNetlistWriter +#define HDR_dbNetlistWriter + +#include "dbCommon.h" +#include "tlTypeTraits.h" + +#include + +namespace tl +{ + class OutputStream; +} + +namespace db +{ + +class Netlist; + +/** + * @brief A common base class for netlist writers + */ +class DB_PUBLIC NetlistWriter +{ +public: + NetlistWriter () { } + virtual ~NetlistWriter () { } + + virtual void write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description = std::string ()) = 0; +}; + +} + +namespace tl +{ + +template <> +struct tl::type_traits + : public tl::type_traits +{ + typedef tl::false_tag has_default_constructor; + typedef tl::false_tag has_copy_constructor; +}; + +} + +#endif diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 67954efcd..c572a3543 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -22,8 +22,11 @@ #include "gsiDecl.h" #include "dbNetlist.h" +#include "dbNetlistWriter.h" +#include "dbNetlistSpiceWriter.h" #include "tlException.h" #include "tlInternational.h" +#include "tlStream.h" namespace gsi { @@ -86,6 +89,10 @@ Class decl_dbDevice ("db", "Device", gsi::method ("name", &db::Device::name, "@brief Gets the name of the device.\n" ) + + gsi::method ("expanded_name", &db::Device::expanded_name, + "@brief Gets the expanded name of the device.\n" + "The expanded name takes the name of the device. If the name is empty, the numeric ID will be used to build a name. " + ) + gsi::method ("net_for_terminal", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_terminal, gsi::arg ("terminal_id"), "@brief Gets the net connected to the specified terminal.\n" "If the terminal is not connected, nil is returned for the net." @@ -209,6 +216,10 @@ Class decl_dbSubCircuit ("db", "SubCircuit", gsi::method ("name", &db::SubCircuit::name, "@brief Gets the name of the subcircuit.\n" ) + + gsi::method ("expanded_name", &db::SubCircuit::expanded_name, + "@brief Gets the expanded name of the subcircuit.\n" + "The expanded name takes the name of the subcircuit. If the name is empty, the numeric ID will be used to build a name. " + ) + gsi::method ("net_for_pin", (db::Net *(db::SubCircuit::*) (size_t)) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"), "@brief Gets the net connected to the specified pin of the subcircuit.\n" "If the pin is not connected, nil is returned for the net." @@ -465,9 +476,15 @@ Class decl_dbDeviceClass ("db", "DeviceClass", gsi::method ("name", &db::DeviceClass::name, "@brief Gets the name of the device class." ) + + gsi::method ("name=", &db::DeviceClass::set_name, gsi::arg ("name"), + "@brief Sets the name of the device class." + ) + gsi::method ("description", &db::DeviceClass::description, "@brief Gets the description text of the device class." ) + + gsi::method ("description=", &db::DeviceClass::set_description, gsi::arg ("description"), + "@brief Sets the description of the device class." + ) + gsi::method ("netlist", (db::Netlist *(db::DeviceClass::*) ()) &db::DeviceClass::netlist, "@brief Gets the netlist the device class lives in." ) + @@ -617,12 +634,6 @@ Class decl_GenericDeviceClass (decl_dbDeviceClass, "db", "Ge gsi::method ("clear_parameters", &GenericDeviceClass::clear_parameter_definitions, "@brief Clears the list of parameters\n" ) + - gsi::method ("name=", &GenericDeviceClass::set_name, gsi::arg ("name"), - "@brief Sets the name of the device\n" - ) + - gsi::method ("description=", &GenericDeviceClass::set_description, gsi::arg ("description"), - "@brief Sets the description of the device\n" - ) + gsi::callback ("combine_devices", &GenericDeviceClass::combine_devices, &GenericDeviceClass::cb_combine_devices, gsi::arg ("a"), gsi::arg ("b"), "@brief Combines two devices.\n" "This method shall test, whether the two devices can be combined. Both devices " @@ -892,18 +903,27 @@ Class decl_dbCircuit ("db", "Circuit", static void add_circuit (db::Netlist *nl, db::Circuit *c) { + tl_assert (c != 0); c->keep (); nl->add_circuit (c); } static void add_device_class (db::Netlist *nl, db::DeviceClass *cl) { + tl_assert (cl != 0); cl->keep (); nl->add_device_class (cl); } +static void write_netlist (const db::Netlist *nl, const std::string &file, db::NetlistWriter *writer, const std::string &description) +{ + tl_assert (writer != 0); + tl::OutputStream os (file); + writer->write (os, *nl, description); +} + Class decl_dbNetlist ("db", "Netlist", - gsi::method_ext ("add", &gsi::add_circuit, + gsi::method_ext ("add", &gsi::add_circuit, gsi::arg ("circuit"), "@brief Adds the circuit to the netlist\n" "This method will add the given circuit object to the netlist. " "After the circuit has been added, it will be owned by the netlist." @@ -938,7 +958,7 @@ Class decl_dbNetlist ("db", "Netlist", gsi::iterator ("each_circuit", (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_circuits, (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_circuits, "@brief Iterates over the circuits of the netlist" ) + - gsi::method_ext ("add", &gsi::add_device_class, + gsi::method_ext ("add", &gsi::add_device_class, gsi::arg ("device_class"), "@brief Adds the device class to the netlist\n" "This method will add the given device class object to the netlist. " "After the device class has been added, it will be owned by the netlist." @@ -978,6 +998,11 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Purges floating nets.\n" "Floating nets can be created as effect of reconnections of devices or pins. " "This method will eliminate all nets that make less than two connections." + ) + + gsi::method_ext ("write", &write_netlist, gsi::arg ("file"), gsi::arg ("writer"), gsi::arg ("description", std::string ()), + "@brief Writes the netlist to the given file using the given writer object to format the file\n" + "See \\NetlistSpiceWriter for an example for a formatter. " + "The description is an arbitrary text which will be put into the file somewhere at the beginning." ), "@brief The netlist top-level class\n" "A netlist is a hierarchical structure of circuits. At least one circuit is the " @@ -991,4 +1016,204 @@ Class decl_dbNetlist ("db", "Netlist", "The netlist class has been introduced with version 0.26." ); +/** + * @brief A SPICE writer delegate base class for reimplementation + */ +class NetlistSpiceWriterDelegateImpl + : public db::NetlistSpiceWriterDelegate, public gsi::ObjectBase +{ +public: + NetlistSpiceWriterDelegateImpl () + : db::NetlistSpiceWriterDelegate () + { + // .. nothing yet .. + } + + virtual void write_header () const + { + if (cb_write_header.can_issue ()) { + cb_write_header.issue (&db::NetlistSpiceWriterDelegate::write_header); + } else { + db::NetlistSpiceWriterDelegate::write_header (); + } + } + + virtual void write_device_intro (const db::DeviceClass &cls) const + { + if (cb_write_device_intro.can_issue ()) { + cb_write_device_intro.issue (&db::NetlistSpiceWriterDelegate::write_device_intro, cls); + } else { + db::NetlistSpiceWriterDelegate::write_device_intro (cls); + } + } + + virtual void write_device (const db::Device &dev) const + { + if (cb_write_device.can_issue ()) { + cb_write_device.issue (&db::NetlistSpiceWriterDelegate::write_device, dev); + } else { + org_write_device (dev); + } + } + + virtual void org_write_device (const db::Device &dev) const + { + db::NetlistSpiceWriterDelegate::write_device (dev); + } + + gsi::Callback cb_write_header; + gsi::Callback cb_write_device_intro; + gsi::Callback cb_write_device; + + using db::NetlistSpiceWriterDelegate::emit_comment; + using db::NetlistSpiceWriterDelegate::emit_line; + using db::NetlistSpiceWriterDelegate::net_to_string; + using db::NetlistSpiceWriterDelegate::format_name; +}; + +Class db_NetlistSpiceWriterDelegate ("db", "NetlistSpiceWriterDelegate", + gsi::callback ("write_header", &NetlistSpiceWriterDelegateImpl::write_header, &NetlistSpiceWriterDelegateImpl::cb_write_header, + "@brief Writes the text at the beginning of the SPICE netlist\n" + "Reimplement this method to insert your own text at the beginning of the file" + ) + + gsi::callback ("write_device_intro", &NetlistSpiceWriterDelegateImpl::write_device_intro, &NetlistSpiceWriterDelegateImpl::cb_write_device_intro, gsi::arg ("device_class"), + "@brief Inserts a text for the given device class\n" + "Reimplement this method to insert your own text at the beginning of the file for the given device class" + ) + + gsi::callback ("write_device", &NetlistSpiceWriterDelegateImpl::write_device, &NetlistSpiceWriterDelegateImpl::cb_write_device, gsi::arg ("device"), + "@brief Inserts a text for the given device\n" + "Reimplement this method to write the given device in the desired way" + ) + + gsi::method ("write_device", &NetlistSpiceWriterDelegateImpl::org_write_device, gsi::arg ("device"), + "@brief Calls the default implementation of the \\write_device method.\n" + "The default implementation will utilize the device class information to write native SPICE " + "elements for the devices." + ) + + gsi::method ("emit_comment", &NetlistSpiceWriterDelegateImpl::emit_comment, gsi::arg ("comment"), + "@brief Writes the given comment into the file" + ) + + gsi::method ("emit_line", &NetlistSpiceWriterDelegateImpl::emit_line, gsi::arg ("line"), + "@brief Writes the given line into the file" + ) + + gsi::method ("net_to_string", &NetlistSpiceWriterDelegateImpl::net_to_string, gsi::arg ("net"), + "@brief Gets the node ID for the given net\n" + "The node ID is a numeric string instead of the full name of the net. Numeric IDs are used within " + "SPICE netlist because they are usually shorter.\n" + ) + + gsi::method ("format_name", &NetlistSpiceWriterDelegateImpl::format_name, gsi::arg ("name"), + "@brief Formats the given name in a SPICE-compatible way" + ), + "@brief Provides a delegate for the SPICE writer for doing special formatting for devices\n" + "Supply a customized class to provide a specialized writing scheme for devices. " + "You need a customized class if you want to implement special devices or you want to use " + "subcircuits rather than the built-in devices.\n" + "\n" + "See \\NetlistSpiceWriter for more details.\n" + "\n" + "This class has been introduced in version 0.26." +); + +namespace { + +class NetlistSpiceWriterWithOwnership + : public db::NetlistSpiceWriter +{ +public: + NetlistSpiceWriterWithOwnership (NetlistSpiceWriterDelegateImpl *delegate) + : db::NetlistSpiceWriter (delegate), m_ownership (delegate) + { + if (delegate) { + delegate->keep (); + } + } + +private: + tl::shared_ptr m_ownership; +}; + +} + +db::NetlistSpiceWriter *new_spice_writer () +{ + return new db::NetlistSpiceWriter (); +} + +db::NetlistSpiceWriter *new_spice_writer2 (NetlistSpiceWriterDelegateImpl *delegate) +{ + return new NetlistSpiceWriterWithOwnership (delegate); +} + +Class db_NetlistWriter ("db", "NetlistWriter", + gsi::Methods (), + "@hide\n" +); + +Class db_NetlistSpiceWriter (db_NetlistWriter, "db", "NetlistSpiceWriter", + gsi::constructor ("new", &new_spice_writer, + "@brief Creates a new writer without delegate.\n" + ) + + gsi::constructor ("new", &new_spice_writer2, + "@brief Creates a new writer with a delegate.\n" + ), + "@brief Implements a netlist writer for the SPICE format.\n" + "Provide a delegate for customizing the way devices are written.\n" + "\n" + "Use the SPICE writer like this:\n" + "\n" + "@code\n" + "writer = RBA::NetlistSpiceWriter::new\n" + "netlist.write(path, writer)\n" + "@endcode\n" + "\n" + "You can give a custom description for the headline:\n" + "\n" + "@code\n" + "writer = RBA::NetlistSpiceWriter::new\n" + "netlist.write(path, writer, \"A custom description\")\n" + "@endcode\n" + "\n" + "To customize the output, you can use a device writer delegate.\n" + "The delegate is an object of a class derived from \\NetlistSpiceWriterDelegate which " + "reimplements several methods to customize the following parts:\n" + "\n" + "@ul\n" + "@li A global header (\\NetlistSpiceWriterDelegate#write_header): this method is called to print the part right after the headline @/li\n" + "@li A per-device class header (\\NetlistSpiceWriterDelegate#write_device_intro): this method is called for every device class and may print device-class specific headers (e.g. model definitions) @/li\n" + "@li Per-device output: this method (\\NetlistSpiceWriterDelegate#write_device): this method is called for every device and may print the device statement(s) in a specific way.\n" + "@/ul\n" + "\n" + "The delegate must use \\NetlistSpiceWriterDelegate#emit_line to print a line, \\NetlistSpiceWriterDelegate#emit_comment to print a comment etc.\n" + "For more method see \\NetlistSpiceWriterDelegate.\n" + "\n" + "A sample with a delegate is this:\n" + "\n" + "@code\n" + "class MyDelegate < RBA::NetlistSpiceWriterDelegate\n" + "\n" + " def write_header\n" + " emit_line(\"*** My special header\")\n" + " end\n" + "\n" + " def write_device_intro(cls)\n" + " emit_comment(\"My intro for class \" + cls.name)\n" + " end\n" + "\n" + " def write_device(dev)\n" + " if dev.device_class.name != \"MYDEVICE\"\n" + " emit_comment(\"Terminal #1: \" + net_to_string(dev.net_for_terminal(0)))\n" + " emit_comment(\"Terminal #2: \" + net_to_string(dev.net_for_terminal(1)))\n" + " super(dev)\n" + " emit_comment(\"After device \" + dev.expanded_name)\n" + " end\n" + "\n" + "end\n" + "\n" + "# write the netlist with delegate:\n" + "writer = RBA::NetlistSpiceWriter::new(MyDelegate::new)\n" + "netlist.write(path, writer)\n" + "@endcode\n" + "\n" + "This class has been introduced in version 0.26." +); + } diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 725573803..4d85928cd 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -1078,3 +1078,4 @@ TEST(13_DeviceAbstract) EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), true); } + diff --git a/src/db/unit_tests/dbNetlistWriterTests.cc b/src/db/unit_tests/dbNetlistWriterTests.cc new file mode 100644 index 000000000..24ec3f413 --- /dev/null +++ b/src/db/unit_tests/dbNetlistWriterTests.cc @@ -0,0 +1,919 @@ + +/* + + 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 "dbNetlistWriter.h" +#include "dbNetlistSpiceWriter.h" +#include "dbNetlist.h" +#include "dbNetlistDeviceClasses.h" + +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" + +TEST(1_WriterResistorDevices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *rdev1 = new db::Device (rcls); + rdev1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.7); + db::Device *rdev2 = new db::Device (rcls); + rdev2->set_parameter_value (db::DeviceClassResistor::param_id_R, 42e-6); + circuit1->add_device (rdev1); + circuit1->add_device (rdev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("A"), n1); + rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("B"), n3); + rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("A"), n3); + rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("B"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter1.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter1_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(2_WriterCapacitorDevices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *cdev1 = new db::Device (ccls); + cdev1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 1.7e-12); + db::Device *cdev2 = new db::Device (ccls); + cdev2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 42e-15); + circuit1->add_device (cdev1); + circuit1->add_device (cdev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("A"), n1); + cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("B"), n3); + cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("A"), n3); + cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("B"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter2.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter2_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(3_WriterInductorDevices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *ldev1 = new db::Device (lcls); + ldev1->set_parameter_value (db::DeviceClassInductor::param_id_L, 1.7e-10); + db::Device *ldev2 = new db::Device (lcls); + ldev2->set_parameter_value (db::DeviceClassInductor::param_id_L, 42e-9); + circuit1->add_device (ldev1); + circuit1->add_device (ldev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + ldev1->connect_terminal (ldev1->device_class ()->terminal_id_for_name ("A"), n1); + ldev1->connect_terminal (ldev1->device_class ()->terminal_id_for_name ("B"), n3); + ldev2->connect_terminal (ldev2->device_class ()->terminal_id_for_name ("A"), n3); + ldev2->connect_terminal (ldev2->device_class ()->terminal_id_for_name ("B"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter3.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter3_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(4_WriterDiodeDevices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *ddev1 = new db::Device (dcls); + ddev1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.7e-10); + db::Device *ddev2 = new db::Device (dcls); + ddev2->set_parameter_value (db::DeviceClassDiode::param_id_A, 42e-9); + circuit1->add_device (ddev1); + circuit1->add_device (ddev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("A"), n1); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("C"), n3); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("A"), n3); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("C"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter4.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter4_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(5_WriterMOS3Devices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3, *n4; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + n4 = new db::Net (); + n4->set_name ("n4"); + circuit1->add_net (n4); + + db::Device *ddev1 = new db::Device (m3cls); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.25); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75); + db::Device *ddev2 = new db::Device (m3cls); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85); + circuit1->add_device (ddev1); + circuit1->add_device (ddev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + size_t pid3 = circuit1->add_pin ("p3").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + circuit1->connect_pin (pid3, n4); + + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n1); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("D"), n3); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("S"), n3); + ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("D"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter5.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter5_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(6_WriterMOS4Devices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3, *n4, *n5; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + n4 = new db::Net (); + n4->set_name ("n4"); + circuit1->add_net (n4); + n5 = new db::Net (); + n5->set_name ("n5"); + circuit1->add_net (n5); + + db::Device *ddev1 = new db::Device (m4cls); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.25); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75); + db::Device *ddev2 = new db::Device (m4cls); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85); + circuit1->add_device (ddev1); + circuit1->add_device (ddev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + size_t pid3 = circuit1->add_pin ("p3").id (); + size_t pid4 = circuit1->add_pin ("p4").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + circuit1->connect_pin (pid3, n4); + circuit1->connect_pin (pid4, n5); + + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n1); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("D"), n3); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("S"), n3); + ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("D"), n2); + ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter6.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter6_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(7_WriterAnyDevices) +{ + db::Netlist nl; + + db::DeviceClass *cls = new db::DeviceClass (); + cls->add_terminal_definition (db::DeviceTerminalDefinition ("A", "a")); + cls->add_terminal_definition (db::DeviceTerminalDefinition ("B", "b")); + cls->add_parameter_definition (db::DeviceParameterDefinition ("U", "u")); + cls->add_parameter_definition (db::DeviceParameterDefinition ("V", "v")); + cls->set_name ("XCLS"); + + nl.add_device_class (cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *ddev1 = new db::Device (cls); + ddev1->set_parameter_value (0, -17); + ddev1->set_parameter_value (1, 42); + db::Device *ddev2 = new db::Device (cls); + ddev2->set_parameter_value (0, 17); + ddev2->set_parameter_value (1, -42); + circuit1->add_device (ddev1); + circuit1->add_device (ddev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("A"), n1); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n3); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("A"), n3); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("B"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter7.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter7_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(8_WriterSubcircuits) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + { + db::Net *n1, *n2, *n3, *n4, *n5; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + n4 = new db::Net (); + n4->set_name ("n4"); + circuit1->add_net (n4); + n5 = new db::Net (); + n5->set_name ("n5"); + circuit1->add_net (n5); + + db::Device *ddev1 = new db::Device (m4cls); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.25); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75); + db::Device *ddev2 = new db::Device (m4cls); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85); + circuit1->add_device (ddev1); + circuit1->add_device (ddev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + size_t pid3 = circuit1->add_pin ("p3").id (); + size_t pid4 = circuit1->add_pin ("p4").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + circuit1->connect_pin (pid3, n4); + circuit1->connect_pin (pid4, n5); + + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n1); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("D"), n3); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("S"), n3); + ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("D"), n2); + ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5); + } + + db::Circuit *circuit2 = new db::Circuit (); + circuit2->set_name ("C2"); + nl.add_circuit (circuit2); + + { + db::Net *n1, *n2, *n3, *n4, *n5; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit2->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit2->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit2->add_net (n3); + n4 = new db::Net (); + n4->set_name ("n4"); + circuit2->add_net (n4); + n5 = new db::Net (); + n5->set_name ("n5"); + circuit2->add_net (n5); + + db::SubCircuit *sc1 = new db::SubCircuit (circuit1, "SC1"); + circuit2->add_subcircuit (sc1); + sc1->connect_pin (0, n1); + sc1->connect_pin (1, n3); + sc1->connect_pin (2, n4); + sc1->connect_pin (3, n3); + + db::SubCircuit *sc2 = new db::SubCircuit (circuit1, "SC2"); + circuit2->add_subcircuit (sc2); + sc2->connect_pin (0, n3); + sc2->connect_pin (1, n2); + sc2->connect_pin (2, n4); + sc2->connect_pin (3, n3); + + size_t pid1 = circuit2->add_pin ("p1").id (); + size_t pid2 = circuit2->add_pin ("p2").id (); + size_t pid3 = circuit2->add_pin ("p3").id (); + + circuit2->connect_pin (pid1, n1); + circuit2->connect_pin (pid2, n2); + circuit2->connect_pin (pid3, n4); + } + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter8.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter8_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(10_WriterLongLines) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + rcls->set_name ("RCLS"); + nl.add_device_class (rcls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway"); + nl.add_circuit (circuit1); + + db::Net *n0 = new db::Net (); + n0->set_name ("n0"); + circuit1->add_net (n0); + + size_t pid0 = circuit1->add_pin ("p0").id (); + circuit1->connect_pin (pid0, n0); + + for (int i = 0; i < 100; ++i) { + + db::Net *n = new db::Net (); + n->set_name ("n" + tl::to_string (i + 1)); + circuit1->add_net (n); + + size_t pid = circuit1->add_pin ("p" + tl::to_string (i + 1)).id (); + circuit1->connect_pin (pid, n); + + db::Device *ddev = new db::Device (rcls); + circuit1->add_device (ddev); + ddev->connect_terminal (db::DeviceClassResistor::terminal_id_A, n0); + ddev->connect_terminal (db::DeviceClassResistor::terminal_id_B, n); + + } + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter10.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter10_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +namespace { + +class MyDelegate + : public db::NetlistSpiceWriterDelegate +{ +public: + MyDelegate () + : db::NetlistSpiceWriterDelegate () + { } + + void write_header () const + { + emit_line ("*** My special header"); + } + + void write_device_intro (const db::DeviceClass &cls) const + { + emit_line ("*** My intro for class " + cls.name ()); + } + + void write_device (const db::Device &dev) const + { + emit_line ("*** Before device " + dev.expanded_name ()); + db::NetlistSpiceWriterDelegate::write_device (dev); + emit_line ("*** After device " + dev.expanded_name ()); + } +}; + +} + +TEST(20_Delegate) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *rdev1 = new db::Device (rcls); + rdev1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.7); + db::Device *rdev2 = new db::Device (rcls); + rdev2->set_parameter_value (db::DeviceClassResistor::param_id_R, 42e-6); + circuit1->add_device (rdev1); + circuit1->add_device (rdev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("A"), n1); + rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("B"), n3); + rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("A"), n3); + rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("B"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter20.txt"); + { + tl::OutputStream stream (path); + MyDelegate delegate; + db::NetlistSpiceWriter writer (&delegate); + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter20_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 460b684a1..df7405b8c 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -65,7 +65,8 @@ SOURCES = \ dbNetlistDeviceClassesTests.cc \ dbLayoutToNetlistTests.cc \ dbLayoutToNetlistWriterTests.cc \ - dbLayoutToNetlistReaderTests.cc + dbLayoutToNetlistReaderTests.cc \ + dbNetlistWriterTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index f3e9cef00..5c6d4175e 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -112,6 +112,7 @@ RUBYTEST (dbLayoutToNetlist, "dbLayoutToNetlist.rb") RUBYTEST (dbMatrix, "dbMatrix.rb") RUBYTEST (dbNetlist, "dbNetlist.rb") RUBYTEST (dbNetlistDeviceClasses, "dbNetlistDeviceClasses.rb") +RUBYTEST (dbNetlistWriterTests, "dbNetlistWriterTests.rb") RUBYTEST (dbPathTest, "dbPathTest.rb") RUBYTEST (dbPCells, "dbPCells.rb") RUBYTEST (dbPointTest, "dbPointTest.rb") diff --git a/testdata/algo/nwriter10_au.txt b/testdata/algo/nwriter10_au.txt new file mode 100644 index 000000000..4b1c13f9d --- /dev/null +++ b/testdata/algo/nwriter10_au.txt @@ -0,0 +1,413 @@ +* written by unit test + +* cell C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway +* pin p0 +* pin p1 +* pin p2 +* pin p3 +* pin p4 +* pin p5 +* pin p6 +* pin p7 +* pin p8 +* pin p9 +* pin p10 +* pin p11 +* pin p12 +* pin p13 +* pin p14 +* pin p15 +* pin p16 +* pin p17 +* pin p18 +* pin p19 +* pin p20 +* pin p21 +* pin p22 +* pin p23 +* pin p24 +* pin p25 +* pin p26 +* pin p27 +* pin p28 +* pin p29 +* pin p30 +* pin p31 +* pin p32 +* pin p33 +* pin p34 +* pin p35 +* pin p36 +* pin p37 +* pin p38 +* pin p39 +* pin p40 +* pin p41 +* pin p42 +* pin p43 +* pin p44 +* pin p45 +* pin p46 +* pin p47 +* pin p48 +* pin p49 +* pin p50 +* pin p51 +* pin p52 +* pin p53 +* pin p54 +* pin p55 +* pin p56 +* pin p57 +* pin p58 +* pin p59 +* pin p60 +* pin p61 +* pin p62 +* pin p63 +* pin p64 +* pin p65 +* pin p66 +* pin p67 +* pin p68 +* pin p69 +* pin p70 +* pin p71 +* pin p72 +* pin p73 +* pin p74 +* pin p75 +* pin p76 +* pin p77 +* pin p78 +* pin p79 +* pin p80 +* pin p81 +* pin p82 +* pin p83 +* pin p84 +* pin p85 +* pin p86 +* pin p87 +* pin p88 +* pin p89 +* pin p90 +* pin p91 +* pin p92 +* pin p93 +* pin p94 +* pin p95 +* pin p96 +* pin p97 +* pin p98 +* pin p99 +* pin p100 +.SUBCKT ++ C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway ++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ++ 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 ++ 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 ++ 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 +* net 1 n0 +* net 2 n1 +* net 3 n2 +* net 4 n3 +* net 5 n4 +* net 6 n5 +* net 7 n6 +* net 8 n7 +* net 9 n8 +* net 10 n9 +* net 11 n10 +* net 12 n11 +* net 13 n12 +* net 14 n13 +* net 15 n14 +* net 16 n15 +* net 17 n16 +* net 18 n17 +* net 19 n18 +* net 20 n19 +* net 21 n20 +* net 22 n21 +* net 23 n22 +* net 24 n23 +* net 25 n24 +* net 26 n25 +* net 27 n26 +* net 28 n27 +* net 29 n28 +* net 30 n29 +* net 31 n30 +* net 32 n31 +* net 33 n32 +* net 34 n33 +* net 35 n34 +* net 36 n35 +* net 37 n36 +* net 38 n37 +* net 39 n38 +* net 40 n39 +* net 41 n40 +* net 42 n41 +* net 43 n42 +* net 44 n43 +* net 45 n44 +* net 46 n45 +* net 47 n46 +* net 48 n47 +* net 49 n48 +* net 50 n49 +* net 51 n50 +* net 52 n51 +* net 53 n52 +* net 54 n53 +* net 55 n54 +* net 56 n55 +* net 57 n56 +* net 58 n57 +* net 59 n58 +* net 60 n59 +* net 61 n60 +* net 62 n61 +* net 63 n62 +* net 64 n63 +* net 65 n64 +* net 66 n65 +* net 67 n66 +* net 68 n67 +* net 69 n68 +* net 70 n69 +* net 71 n70 +* net 72 n71 +* net 73 n72 +* net 74 n73 +* net 75 n74 +* net 76 n75 +* net 77 n76 +* net 78 n77 +* net 79 n78 +* net 80 n79 +* net 81 n80 +* net 82 n81 +* net 83 n82 +* net 84 n83 +* net 85 n84 +* net 86 n85 +* net 87 n86 +* net 88 n87 +* net 89 n88 +* net 90 n89 +* net 91 n90 +* net 92 n91 +* net 93 n92 +* net 94 n93 +* net 95 n94 +* net 96 n95 +* net 97 n96 +* net 98 n97 +* net 99 n98 +* net 100 n99 +* net 101 n100 +* device instance $1 0,0 RCLS +R$1 1 2 0 +* device instance $2 0,0 RCLS +R$2 1 3 0 +* device instance $3 0,0 RCLS +R$3 1 4 0 +* device instance $4 0,0 RCLS +R$4 1 5 0 +* device instance $5 0,0 RCLS +R$5 1 6 0 +* device instance $6 0,0 RCLS +R$6 1 7 0 +* device instance $7 0,0 RCLS +R$7 1 8 0 +* device instance $8 0,0 RCLS +R$8 1 9 0 +* device instance $9 0,0 RCLS +R$9 1 10 0 +* device instance $10 0,0 RCLS +R$10 1 11 0 +* device instance $11 0,0 RCLS +R$11 1 12 0 +* device instance $12 0,0 RCLS +R$12 1 13 0 +* device instance $13 0,0 RCLS +R$13 1 14 0 +* device instance $14 0,0 RCLS +R$14 1 15 0 +* device instance $15 0,0 RCLS +R$15 1 16 0 +* device instance $16 0,0 RCLS +R$16 1 17 0 +* device instance $17 0,0 RCLS +R$17 1 18 0 +* device instance $18 0,0 RCLS +R$18 1 19 0 +* device instance $19 0,0 RCLS +R$19 1 20 0 +* device instance $20 0,0 RCLS +R$20 1 21 0 +* device instance $21 0,0 RCLS +R$21 1 22 0 +* device instance $22 0,0 RCLS +R$22 1 23 0 +* device instance $23 0,0 RCLS +R$23 1 24 0 +* device instance $24 0,0 RCLS +R$24 1 25 0 +* device instance $25 0,0 RCLS +R$25 1 26 0 +* device instance $26 0,0 RCLS +R$26 1 27 0 +* device instance $27 0,0 RCLS +R$27 1 28 0 +* device instance $28 0,0 RCLS +R$28 1 29 0 +* device instance $29 0,0 RCLS +R$29 1 30 0 +* device instance $30 0,0 RCLS +R$30 1 31 0 +* device instance $31 0,0 RCLS +R$31 1 32 0 +* device instance $32 0,0 RCLS +R$32 1 33 0 +* device instance $33 0,0 RCLS +R$33 1 34 0 +* device instance $34 0,0 RCLS +R$34 1 35 0 +* device instance $35 0,0 RCLS +R$35 1 36 0 +* device instance $36 0,0 RCLS +R$36 1 37 0 +* device instance $37 0,0 RCLS +R$37 1 38 0 +* device instance $38 0,0 RCLS +R$38 1 39 0 +* device instance $39 0,0 RCLS +R$39 1 40 0 +* device instance $40 0,0 RCLS +R$40 1 41 0 +* device instance $41 0,0 RCLS +R$41 1 42 0 +* device instance $42 0,0 RCLS +R$42 1 43 0 +* device instance $43 0,0 RCLS +R$43 1 44 0 +* device instance $44 0,0 RCLS +R$44 1 45 0 +* device instance $45 0,0 RCLS +R$45 1 46 0 +* device instance $46 0,0 RCLS +R$46 1 47 0 +* device instance $47 0,0 RCLS +R$47 1 48 0 +* device instance $48 0,0 RCLS +R$48 1 49 0 +* device instance $49 0,0 RCLS +R$49 1 50 0 +* device instance $50 0,0 RCLS +R$50 1 51 0 +* device instance $51 0,0 RCLS +R$51 1 52 0 +* device instance $52 0,0 RCLS +R$52 1 53 0 +* device instance $53 0,0 RCLS +R$53 1 54 0 +* device instance $54 0,0 RCLS +R$54 1 55 0 +* device instance $55 0,0 RCLS +R$55 1 56 0 +* device instance $56 0,0 RCLS +R$56 1 57 0 +* device instance $57 0,0 RCLS +R$57 1 58 0 +* device instance $58 0,0 RCLS +R$58 1 59 0 +* device instance $59 0,0 RCLS +R$59 1 60 0 +* device instance $60 0,0 RCLS +R$60 1 61 0 +* device instance $61 0,0 RCLS +R$61 1 62 0 +* device instance $62 0,0 RCLS +R$62 1 63 0 +* device instance $63 0,0 RCLS +R$63 1 64 0 +* device instance $64 0,0 RCLS +R$64 1 65 0 +* device instance $65 0,0 RCLS +R$65 1 66 0 +* device instance $66 0,0 RCLS +R$66 1 67 0 +* device instance $67 0,0 RCLS +R$67 1 68 0 +* device instance $68 0,0 RCLS +R$68 1 69 0 +* device instance $69 0,0 RCLS +R$69 1 70 0 +* device instance $70 0,0 RCLS +R$70 1 71 0 +* device instance $71 0,0 RCLS +R$71 1 72 0 +* device instance $72 0,0 RCLS +R$72 1 73 0 +* device instance $73 0,0 RCLS +R$73 1 74 0 +* device instance $74 0,0 RCLS +R$74 1 75 0 +* device instance $75 0,0 RCLS +R$75 1 76 0 +* device instance $76 0,0 RCLS +R$76 1 77 0 +* device instance $77 0,0 RCLS +R$77 1 78 0 +* device instance $78 0,0 RCLS +R$78 1 79 0 +* device instance $79 0,0 RCLS +R$79 1 80 0 +* device instance $80 0,0 RCLS +R$80 1 81 0 +* device instance $81 0,0 RCLS +R$81 1 82 0 +* device instance $82 0,0 RCLS +R$82 1 83 0 +* device instance $83 0,0 RCLS +R$83 1 84 0 +* device instance $84 0,0 RCLS +R$84 1 85 0 +* device instance $85 0,0 RCLS +R$85 1 86 0 +* device instance $86 0,0 RCLS +R$86 1 87 0 +* device instance $87 0,0 RCLS +R$87 1 88 0 +* device instance $88 0,0 RCLS +R$88 1 89 0 +* device instance $89 0,0 RCLS +R$89 1 90 0 +* device instance $90 0,0 RCLS +R$90 1 91 0 +* device instance $91 0,0 RCLS +R$91 1 92 0 +* device instance $92 0,0 RCLS +R$92 1 93 0 +* device instance $93 0,0 RCLS +R$93 1 94 0 +* device instance $94 0,0 RCLS +R$94 1 95 0 +* device instance $95 0,0 RCLS +R$95 1 96 0 +* device instance $96 0,0 RCLS +R$96 1 97 0 +* device instance $97 0,0 RCLS +R$97 1 98 0 +* device instance $98 0,0 RCLS +R$98 1 99 0 +* device instance $99 0,0 RCLS +R$99 1 100 0 +* device instance $100 0,0 RCLS +R$100 1 101 0 +.ENDS ++ C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway diff --git a/testdata/algo/nwriter1_au.txt b/testdata/algo/nwriter1_au.txt new file mode 100644 index 000000000..54205fa66 --- /dev/null +++ b/testdata/algo/nwriter1_au.txt @@ -0,0 +1,14 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 RCLS +R$1 1 3 1.7 +* device instance $2 0,0 RCLS +R$2 3 2 4.2e-05 +.ENDS C1 diff --git a/testdata/algo/nwriter20_au.txt b/testdata/algo/nwriter20_au.txt new file mode 100644 index 000000000..f120641fa --- /dev/null +++ b/testdata/algo/nwriter20_au.txt @@ -0,0 +1,25 @@ +* written by unit test +*** My special header +*** My intro for class RCLS +*** My intro for class LCLS +*** My intro for class CCLS +*** My intro for class DCLS +*** My intro for class M3CLS +*** My intro for class M4CLS + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 RCLS +*** Before device $1 +R$1 1 3 1.7 +*** After device $1 +* device instance $2 0,0 RCLS +*** Before device $2 +R$2 3 2 4.2e-05 +*** After device $2 +.ENDS C1 diff --git a/testdata/algo/nwriter2_au.txt b/testdata/algo/nwriter2_au.txt new file mode 100644 index 000000000..661626646 --- /dev/null +++ b/testdata/algo/nwriter2_au.txt @@ -0,0 +1,14 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 CCLS +C$1 1 3 1.7e-12 +* device instance $2 0,0 CCLS +C$2 3 2 4.2e-14 +.ENDS C1 diff --git a/testdata/algo/nwriter3_au.txt b/testdata/algo/nwriter3_au.txt new file mode 100644 index 000000000..acaf6fb6a --- /dev/null +++ b/testdata/algo/nwriter3_au.txt @@ -0,0 +1,14 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 LCLS +L$1 1 3 1.7e-10 +* device instance $2 0,0 LCLS +L$2 3 2 4.2e-08 +.ENDS C1 diff --git a/testdata/algo/nwriter4_au.txt b/testdata/algo/nwriter4_au.txt new file mode 100644 index 000000000..8622bc7f1 --- /dev/null +++ b/testdata/algo/nwriter4_au.txt @@ -0,0 +1,14 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 DCLS +D$1 1 3 DDCLS +* device instance $2 0,0 DCLS +D$2 3 2 DDCLS +.ENDS C1 diff --git a/testdata/algo/nwriter5_au.txt b/testdata/algo/nwriter5_au.txt new file mode 100644 index 000000000..2f6212938 --- /dev/null +++ b/testdata/algo/nwriter5_au.txt @@ -0,0 +1,16 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +* pin p3 +.SUBCKT C1 1 2 4 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* net 4 n4 +* device instance $1 0,0 M3CLS +M$1 1 4 3 1 MM3CLS L=0.25U W=0.18U AS=1.2U AD=0.75U +* device instance $2 0,0 M3CLS +M$2 3 4 2 3 MM3CLS L=1.4U W=0.25U AS=1.3U AD=0.85U +.ENDS C1 diff --git a/testdata/algo/nwriter6_au.txt b/testdata/algo/nwriter6_au.txt new file mode 100644 index 000000000..caae620b9 --- /dev/null +++ b/testdata/algo/nwriter6_au.txt @@ -0,0 +1,18 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +* pin p3 +* pin p4 +.SUBCKT C1 1 2 4 5 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* net 4 n4 +* net 5 n5 +* device instance $1 0,0 M4CLS +M$1 1 4 3 5 1 MM4CLS L=0.25U W=0.18U AS=1.2U AD=0.75U +* device instance $2 0,0 M4CLS +M$2 3 4 2 5 3 MM4CLS L=1.4U W=0.25U AS=1.3U AD=0.85U +.ENDS C1 diff --git a/testdata/algo/nwriter7_au.txt b/testdata/algo/nwriter7_au.txt new file mode 100644 index 000000000..8b4656dca --- /dev/null +++ b/testdata/algo/nwriter7_au.txt @@ -0,0 +1,14 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 XCLS +XD_$1 1 3 XCLS PARAMS: U=-17 V=42 +* device instance $2 0,0 XCLS +XD_$2 3 2 XCLS PARAMS: U=17 V=-42 +.ENDS C1 diff --git a/testdata/algo/nwriter8_au.txt b/testdata/algo/nwriter8_au.txt new file mode 100644 index 000000000..b17e53ee2 --- /dev/null +++ b/testdata/algo/nwriter8_au.txt @@ -0,0 +1,34 @@ +* written by unit test + +* cell C2 +* pin p1 +* pin p2 +* pin p3 +.SUBCKT C2 1 2 4 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* net 4 n4 +* net 5 n5 +* cell instance SC1 r0 *1 0,0 +XSC1 1 3 4 3 C1 +* cell instance SC2 r0 *1 0,0 +XSC2 3 2 4 3 C1 +.ENDS C2 + +* cell C1 +* pin p1 +* pin p2 +* pin p3 +* pin p4 +.SUBCKT C1 1 2 4 5 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* net 4 n4 +* net 5 n5 +* device instance $1 0,0 M4CLS +M$1 1 4 3 5 1 MM4CLS L=0.25U W=0.18U AS=1.2U AD=0.75U +* device instance $2 0,0 M4CLS +M$2 3 4 2 5 3 MM4CLS L=1.4U W=0.25U AS=1.3U AD=0.85U +.ENDS C1 diff --git a/testdata/algo/nwriter_rba1_au.txt b/testdata/algo/nwriter_rba1_au.txt new file mode 100644 index 000000000..17383a7b0 --- /dev/null +++ b/testdata/algo/nwriter_rba1_au.txt @@ -0,0 +1,13 @@ + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 RCLS +R$1 1 3 1.7 +* device instance $2 0,0 RCLS +R$2 3 2 4.2e-05 +.ENDS C1 diff --git a/testdata/algo/nwriter_rba2_au.txt b/testdata/algo/nwriter_rba2_au.txt new file mode 100644 index 000000000..6e4a8506b --- /dev/null +++ b/testdata/algo/nwriter_rba2_au.txt @@ -0,0 +1,29 @@ +* A comment +*** My special header +* My intro for class RCLS +* My intro for class LCLS +* My intro for class CCLS +* My intro for class DCLS +* My intro for class M3CLS +* My intro for class M4CLS + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 RCLS +* Before device $0 +* Terminal #1: 0 +* Terminal #2: 0 +R$0 0 0 1.7 +* After device $0 +* device instance $2 0,0 RCLS +* Before device $0 +* Terminal #1: 0 +* Terminal #2: 0 +R$0 0 0 4.2e-05 +* After device $0 +.ENDS C1 diff --git a/testdata/ruby/dbNetlistWriterTests.rb b/testdata/ruby/dbNetlistWriterTests.rb new file mode 100644 index 000000000..5e02a0561 --- /dev/null +++ b/testdata/ruby/dbNetlistWriterTests.rb @@ -0,0 +1,127 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2019 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class MyDelegate < RBA::NetlistSpiceWriterDelegate + + def write_header + emit_line("*** My special header") + end + + def write_device_intro(cls) + emit_comment("My intro for class " + cls.name) + end + + def write_device(dev) + emit_comment("Before device " + dev.expanded_name) + emit_comment("Terminal #1: " + net_to_string(dev.net_for_terminal(0))) + emit_comment("Terminal #2: " + net_to_string(dev.net_for_terminal(1))) + super(dev) + emit_comment("After device " + dev.expanded_name) + end + +end + +class DBLayoutToNetlist_TestClass < TestBase + + def test_1_Basic + + nl = RBA::Netlist::new + + rcls = RBA::DeviceClassResistor::new + ccls = RBA::DeviceClassCapacitor::new + lcls = RBA::DeviceClassInductor::new + dcls = RBA::DeviceClassDiode::new + m3cls = RBA::DeviceClassMOS3Transistor::new + m4cls = RBA::DeviceClassMOS4Transistor::new + + rcls.name = "RCLS" + lcls.name = "LCLS" + ccls.name = "CCLS" + dcls.name = "DCLS" + m3cls.name = "M3CLS" + m4cls.name = "M4CLS" + + nl.add(rcls) + nl.add(lcls) + nl.add(ccls) + nl.add(dcls) + nl.add(m3cls) + nl.add(m4cls) + + circuit1 = RBA::Circuit::new + circuit1.name = "C1" + nl.add(circuit1) + + n1 = circuit1.create_net("n1") + n2 = circuit1.create_net("n2") + n3 = circuit1.create_net("n3") + + rdev1 = circuit1.create_device(rcls) + rdev1.set_parameter(RBA::DeviceClassResistor::PARAM_R, 1.7) + rdev2 = circuit1.create_device(rcls) + rdev2.set_parameter(RBA::DeviceClassResistor::PARAM_R, 42e-6) + + pid1 = circuit1.create_pin("p1").id + pid2 = circuit1.create_pin("p2").id + + circuit1.connect_pin(pid1, n1) + circuit1.connect_pin(pid2, n2) + + rdev1.connect_terminal("A", n1) + rdev1.connect_terminal("B", n3) + rdev2.connect_terminal("A", n3) + rdev2.connect_terminal("B", n2) + + # verify against the input + + input = File.join($ut_testsrc, "testdata", "algo", "nwriter_rba1_au.txt") + + writer = RBA::NetlistSpiceWriter::new + tmp = File::join($ut_testtmp, "tmp1.txt") + nl.write(tmp, writer) + + assert_equal(File.open(tmp, "r").read, File.open(input, "r").read) + + # verify against the input with delegate + + input = File.join($ut_testsrc, "testdata", "algo", "nwriter_rba2_au.txt") + + mydelegate = MyDelegate::new + writer = RBA::NetlistSpiceWriter::new(mydelegate) + # the delegate is kept by the SPICE writer .. + mydelegate = nil + GC.start + tmp = File::join($ut_testtmp, "tmp2.txt") + nl.write(tmp, writer, "A comment") + + assert_equal(File.open(tmp, "r").read, File.open(input, "r").read) + + end + +end + +load("test_epilogue.rb") + + From 30e26c4f962b0b7b8e5e0909fc3a349c8ee4fa1e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 31 Jan 2019 00:36:44 +0100 Subject: [PATCH 225/335] Avoid an issue with virtual functions Reimplementing virtual functions with "const &" arguments wasn't behaving as expected because these arguments were copied. Now, "const &" for arguments (in virtual function reimplementation) is not implemented as a copy. In addition, now it's possible to declare results as references always (also if const &). See gsiTest.cc:1078 for example: // gsi::arg_make_reference makes the function's return value // always being taken as a reference gsi::method ("pass_cd_cref_as_ref", &C_P::pass_cd_cref) --- src/gsi/gsi/gsiMethods.h | 73 ++++++++++---- src/gsi/gsi/gsiMethodsVar.h | 132 ++++++++++++++++--------- src/gsi/gsi/gsiTypes.cc | 7 +- src/gsi/gsi/gsiTypes.h | 165 ++++++++++++++++++++++++++++++-- src/gsi/gsi_test/gsiTest.cc | 25 +++++ src/gsi/gsi_test/gsiTest.h | 51 ++++++++++ src/pya/pya/pyaConvert.cc | 2 +- src/rba/rba/rbaConvert.cc | 2 +- testdata/python/basic.py | 74 ++++++++++++++ testdata/ruby/basic_testcore.rb | 84 ++++++++++++++++ 10 files changed, 539 insertions(+), 76 deletions(-) diff --git a/src/gsi/gsi/gsiMethods.h b/src/gsi/gsi/gsiMethods.h index 472748710..9d214ef6f 100644 --- a/src/gsi/gsi/gsiMethods.h +++ b/src/gsi/gsi/gsiMethods.h @@ -334,11 +334,23 @@ public: /** * @brief Adds an argument to the argument list (of type X) */ - template + template void add_arg () { ArgType a; - a.template init (); + a.template init (); + m_arg_types.push_back (a); + m_argsize += a.size (); + } + + /** + * @brief Adds an argument to the argument list (of type X) + */ + template + void add_arg () + { + ArgType a; + a.template init (); m_arg_types.push_back (a); m_argsize += a.size (); } @@ -346,11 +358,23 @@ public: /** * @brief Adds an argument to the argument list (of type X plus additional specs) */ - template + template void add_arg (const ArgSpecBase &spec) { ArgType a; - a.template init (spec); + a.template init (spec); + m_arg_types.push_back (a); + m_argsize += a.size (); + } + + /** + * @brief Adds an argument to the argument list (of type X plus additional specs) + */ + template + void add_arg (const ArgSpecBase &spec) + { + ArgType a; + a.template init (spec); m_arg_types.push_back (a); m_argsize += a.size (); } @@ -358,11 +382,23 @@ public: /** * @brief This version will take the ownership of the ArgSpecBase object */ - template + template void add_arg (ArgSpecBase *spec) { ArgType a; - a.template init (spec); + a.template init (spec); + m_arg_types.push_back (a); + m_argsize += a.size (); + } + + /** + * @brief This version will take the ownership of the ArgSpecBase object + */ + template + void add_arg (ArgSpecBase *spec) + { + ArgType a; + a.template init (spec); m_arg_types.push_back (a); m_argsize += a.size (); } @@ -379,10 +415,19 @@ public: /** * @brief Sets the return type to "X" */ - template + template void set_return () { - m_ret_type.template init (); + m_ret_type.template init (); + } + + /** + * @brief Sets the return type to "X" + */ + template + void set_return () + { + m_ret_type.template init (); } /** @@ -391,7 +436,7 @@ public: template void set_return_new () { - m_ret_type.template init (true); + m_ret_type.template init (); } /** @@ -853,16 +898,6 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str return Methods (new ConstantValueGetter (name, v, doc)); } -struct return_by_value -{ - typedef tl::False is_factory; -}; - -struct return_new_object -{ - typedef tl::True is_factory; -}; - // 0 argument #define _COUNT 0 diff --git a/src/gsi/gsi/gsiMethodsVar.h b/src/gsi/gsi/gsiMethodsVar.h index db383beaf..c0511038f 100644 --- a/src/gsi/gsi/gsiMethodsVar.h +++ b/src/gsi/gsi/gsiMethodsVar.h @@ -220,7 +220,7 @@ private: _ARGSPECMEM }; -template +template class _NAME(Method) : public MethodSpecificBase { @@ -240,11 +240,7 @@ public: { this->clear (); _ADDARGS - if (tl::value_from_type (typename F::is_factory ())) { - this->template set_return_new (); - } else { - this->template set_return (); - } + this->template set_return (); } virtual MethodBase *clone () const @@ -268,7 +264,7 @@ private: _ARGSPECMEM }; -template +template class _NAME(ConstMethod) : public MethodSpecificBase { @@ -288,11 +284,7 @@ public: { this->clear (); _ADDARGS - if (tl::value_from_type (typename F::is_factory ())) { - this->template set_return_new (); - } else { - this->template set_return (); - } + this->template set_return (); } virtual MethodBase *clone () const @@ -316,7 +308,7 @@ private: _ARGSPECMEM }; -template +template class _NAME(ExtMethod) : public MethodBase { @@ -336,11 +328,7 @@ public: { this->clear (); _ADDARGS - if (tl::value_from_type (typename F::is_factory ())) { - this->template set_return_new (); - } else { - this->template set_return (); - } + this->template set_return (); } virtual MethodBase *clone () const @@ -364,7 +352,7 @@ private: _ARGSPECMEM }; -template +template class _NAME(StaticMethod) : public StaticMethodBase { @@ -384,11 +372,7 @@ public: { this->clear (); _ADDARGS - if (tl::value_from_type (typename F::is_factory ())) { - this->template set_return_new (); - } else { - this->template set_return (); - } + this->template set_return (); } virtual MethodBase *clone () const @@ -1300,7 +1284,14 @@ template Methods method (const std::string &name, R (X::*m) (_FUNCARGLIST), const std::string &doc = std::string ()) { - return Methods (new _NAME(Method) (name, m, doc)); + return Methods (new _NAME(Method) (name, m, doc)); +} + +template +Methods +method (const std::string &name, R (X::*m) (_FUNCARGLIST), const std::string &doc = std::string ()) +{ + return Methods (new _NAME(Method) (name, m, doc)); } #if _COUNT != 0 @@ -1308,7 +1299,14 @@ template Methods method (const std::string &name, R (X::*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(Method) (name, m, doc))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(Method) (name, m, doc))->add_args (_ARGSPECARGS)); +} + +template +Methods +method (const std::string &name, R (X::*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ()) +{ + return Methods ((new _NAME(Method) (name, m, doc))->add_args (_ARGSPECARGS)); } #endif @@ -1316,7 +1314,7 @@ template Methods factory (const std::string &name, R *(X::*m) (_FUNCARGLIST), const std::string &doc = std::string ()) { - return Methods (new _NAME(Method) (name, m, doc)); + return Methods (new _NAME(Method) (name, m, doc)); } #if _COUNT != 0 @@ -1324,7 +1322,7 @@ template Methods factory (const std::string &name, R *(X::*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(Method) (name, m, doc))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(Method) (name, m, doc))->add_args (_ARGSPECARGS)); } #endif @@ -1332,7 +1330,14 @@ template Methods method_ext (const std::string &name, R (*xm) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ()) { - return Methods (new _NAME(ExtMethod) (name, xm, doc)); + return Methods (new _NAME(ExtMethod) (name, xm, doc)); +} + +template +Methods +method_ext (const std::string &name, R (*xm) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ()) +{ + return Methods (new _NAME(ExtMethod) (name, xm, doc)); } #if _COUNT != 0 @@ -1340,7 +1345,14 @@ template Methods method_ext (const std::string &name, R (*xm) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(ExtMethod) (name, xm, doc))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(ExtMethod) (name, xm, doc))->add_args (_ARGSPECARGS)); +} + +template +Methods +method_ext (const std::string &name, R (*xm) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ()) +{ + return Methods ((new _NAME(ExtMethod) (name, xm, doc))->add_args (_ARGSPECARGS)); } #endif @@ -1348,7 +1360,7 @@ template Methods factory_ext (const std::string &name, R *(*xm) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ()) { - return Methods (new _NAME(ExtMethod) (name, xm, doc)); + return Methods (new _NAME(ExtMethod) (name, xm, doc)); } #if _COUNT != 0 @@ -1356,7 +1368,7 @@ template Methods factory_ext (const std::string &name, R *(*xm) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(ExtMethod) (name, xm, doc))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(ExtMethod) (name, xm, doc))->add_args (_ARGSPECARGS)); } #endif @@ -1364,7 +1376,7 @@ template Methods constructor (const std::string &name, X *(*m) (_FUNCARGLIST), const std::string &doc = std::string ()) { - return Methods (new _NAME(StaticMethod) (name, m, doc)); + return Methods (new _NAME(StaticMethod) (name, m, doc)); } #if _COUNT != 0 @@ -1372,7 +1384,7 @@ template Methods constructor (const std::string &name, X *(*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(StaticMethod) (name, m, doc))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(StaticMethod) (name, m, doc))->add_args (_ARGSPECARGS)); } #endif @@ -1380,7 +1392,14 @@ template Methods method (const std::string &name, R (*m) (_FUNCARGLIST), const std::string &doc = std::string ()) { - return Methods (new _NAME(StaticMethod) (name, m, doc)); + return Methods (new _NAME(StaticMethod) (name, m, doc)); +} + +template +Methods +method (const std::string &name, R (*m) (_FUNCARGLIST), const std::string &doc = std::string ()) +{ + return Methods (new _NAME(StaticMethod) (name, m, doc)); } #if _COUNT != 0 @@ -1388,7 +1407,14 @@ template Methods method (const std::string &name, R (*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(StaticMethod) (name, m, doc))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(StaticMethod) (name, m, doc))->add_args (_ARGSPECARGS)); +} + +template +Methods +method (const std::string &name, R (*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ()) +{ + return Methods ((new _NAME(StaticMethod) (name, m, doc))->add_args (_ARGSPECARGS)); } #endif @@ -1396,7 +1422,7 @@ template Methods factory (const std::string &name, R *(*m) (_FUNCARGLIST), const std::string &doc = std::string ()) { - return Methods (new _NAME(StaticMethod) (name, m, doc)); + return Methods (new _NAME(StaticMethod) (name, m, doc)); } #if _COUNT != 0 @@ -1404,7 +1430,7 @@ template Methods factory (const std::string &name, R *(*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(StaticMethod) (name, m, doc))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(StaticMethod) (name, m, doc))->add_args (_ARGSPECARGS)); } #endif @@ -1428,7 +1454,7 @@ template Methods factory_callback (const std::string &name, R (X::*m) (_FUNCARGLIST), Callback X::*cb, const std::string &doc = std::string ()) { - return Methods (new _NAME(Method) (name, m, doc, cb)); + return Methods (new _NAME(Method) (name, m, doc, cb)); } #if _COUNT != 0 @@ -1436,7 +1462,7 @@ template Methods factory_callback (const std::string &name, R (X::*m) (_FUNCARGLIST), Callback X::*cb _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(Method) (name, m, doc, cb))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(Method) (name, m, doc, cb))->add_args (_ARGSPECARGS)); } #endif @@ -1444,7 +1470,14 @@ template Methods method (const std::string &name, R (X::*m) (_FUNCARGLIST) const, const std::string &doc = std::string ()) { - return Methods (new _NAME(ConstMethod) (name, m, doc)); + return Methods (new _NAME(ConstMethod) (name, m, doc)); +} + +template +Methods +method (const std::string &name, R (X::*m) (_FUNCARGLIST) const, const std::string &doc = std::string ()) +{ + return Methods (new _NAME(ConstMethod) (name, m, doc)); } #if _COUNT != 0 @@ -1452,7 +1485,14 @@ template Methods method (const std::string &name, R (X::*m) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(ConstMethod) (name, m, doc))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(ConstMethod) (name, m, doc))->add_args (_ARGSPECARGS)); +} + +template +Methods +method (const std::string &name, R (X::*m) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ()) +{ + return Methods ((new _NAME(ConstMethod) (name, m, doc))->add_args (_ARGSPECARGS)); } #endif @@ -1460,7 +1500,7 @@ template Methods factory (const std::string &name, R *(X::*m) (_FUNCARGLIST) const, const std::string &doc = std::string ()) { - return Methods (new _NAME(ConstMethod) (name, m, doc)); + return Methods (new _NAME(ConstMethod) (name, m, doc)); } #if _COUNT != 0 @@ -1468,7 +1508,7 @@ template Methods factory (const std::string &name, R *(X::*m) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(ConstMethod) (name, m, doc))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(ConstMethod) (name, m, doc))->add_args (_ARGSPECARGS)); } #endif @@ -1492,7 +1532,7 @@ template Methods factory_callback (const std::string &name, R (X::*m) (_FUNCARGLIST) const, Callback X::*cb, const std::string &doc = std::string ()) { - return Methods (new _NAME(ConstMethod) (name, m, doc, cb)); + return Methods (new _NAME(ConstMethod) (name, m, doc, cb)); } #if _COUNT != 0 @@ -1500,7 +1540,7 @@ template Methods factory_callback (const std::string &name, R (X::*m) (_FUNCARGLIST) const, Callback X::*cb _COMMA _ARGSPECS, const std::string &doc = std::string ()) { - return Methods ((new _NAME(ConstMethod) (name, m, doc, cb))->add_args (_ARGSPECARGS)); + return Methods ((new _NAME(ConstMethod) (name, m, doc, cb))->add_args (_ARGSPECARGS)); } #endif diff --git a/src/gsi/gsi/gsiTypes.cc b/src/gsi/gsi/gsiTypes.cc index c79711b86..237e154ac 100644 --- a/src/gsi/gsi/gsiTypes.cc +++ b/src/gsi/gsi/gsiTypes.cc @@ -112,7 +112,7 @@ ArgType::to_string () const ArgType::ArgType () : m_type (T_void), mp_spec (0), mp_inner (0), mp_inner_k (0), m_is_ref (false), m_is_ptr (false), m_is_cref (false), m_is_cptr (false), m_is_iter (false), - m_owns_spec (false), m_pass_obj (false), + m_owns_spec (false), m_pass_obj (false), m_prefer_copy (false), mp_cls (0), m_size (0) { } @@ -132,7 +132,7 @@ ArgType::~ArgType () ArgType::ArgType (const ArgType &other) : m_type (T_void), mp_spec (0), mp_inner (0), mp_inner_k (0), m_is_ref (false), m_is_ptr (false), m_is_cref (false), m_is_cptr (false), m_is_iter (false), - m_owns_spec (false), m_pass_obj (false), + m_owns_spec (false), m_pass_obj (false), m_prefer_copy (false), mp_cls (0), m_size (0) { operator= (other); @@ -156,6 +156,7 @@ ArgType::operator= (const ArgType &other) m_type = other.m_type; m_pass_obj = other.m_pass_obj; + m_prefer_copy = other.m_prefer_copy; m_is_ref = other.m_is_ref; m_is_cref = other.m_is_cref; m_is_ptr = other.m_is_ptr; @@ -204,7 +205,7 @@ ArgType::operator== (const ArgType &b) const } return m_type == b.m_type && m_is_iter == b.m_is_iter && m_is_ref == b.m_is_ref && m_is_cref == b.m_is_cref && m_is_ptr == b.m_is_ptr && m_is_cptr == b.m_is_cptr && - mp_cls == b.mp_cls && m_pass_obj == b.m_pass_obj; + mp_cls == b.mp_cls && m_pass_obj == b.m_pass_obj && m_prefer_copy == b.m_prefer_copy; } void diff --git a/src/gsi/gsi/gsiTypes.h b/src/gsi/gsi/gsiTypes.h index 3347772be..4f4065285 100644 --- a/src/gsi/gsi/gsiTypes.h +++ b/src/gsi/gsi/gsiTypes.h @@ -1385,6 +1385,67 @@ public: } }; +/** + * @brief A tag indicating ownership transfer + * This tag will transfer ownership of an object (either back- or forward). + */ +struct arg_pass_ownership { }; + +/** + * @brief A tag indicating copy preference + * By specifying this tag, making a copy of the object is the preferred transfer method + */ +struct arg_make_copy { }; + +/** + * @brief A tag indicating to take the reference of an object + * By specifying this tag, taking a reference is the preferred transfer method + */ +struct arg_make_reference { }; + +/** + * @brief A tag indicating the default return value preference + * The default return value preference is "copy" for const references and direct values + * and reference otherwise. + */ +struct arg_default_return_value_preference { }; + +/** + * @brief A function computing the "prefer_copy" value + */ +template +struct compute_prefer_copy +{ + static bool value () { return false; } +}; + +template +struct compute_prefer_copy +{ + static bool value () { return type_traits::is_cref (); } +}; + +template +struct compute_prefer_copy +{ + static bool value () { return true; } +}; + +/** + * @brief A function computing the "pass_obj" value + */ +template +struct compute_pass_obj +{ + static bool value () { return false; } +}; + +template +struct compute_pass_obj +{ + static bool value () { return true; } +}; + /** * @brief Generic argument type declaration * @@ -1427,7 +1488,22 @@ public: template void init (const ArgSpecBase &spec) { - init (); + init (); + mp_spec = &spec; + m_owns_spec = false; + } + + /** + * @brief Initializes with an external specification + * + * This method will initialize the type object referring to an external + * spec object. The spec object will specify name and default + * value for arguments + */ + template + void init (const ArgSpecBase &spec) + { + init (); mp_spec = &spec; m_owns_spec = false; } @@ -1442,7 +1518,22 @@ public: template void init (ArgSpecBase *spec) { - init (); + init (); + mp_spec = spec; + m_owns_spec = true; + } + + /** + * @brief Initializes with a specification + * + * This method will initialize the type object with a specification. + * The ownership over the specification object will be transferred to + * the type object. + */ + template + void init (ArgSpecBase *spec) + { + init (); mp_spec = spec; m_owns_spec = true; } @@ -1454,7 +1545,7 @@ public: * to the case of object pointers mainly. */ template - void init (bool pass_obj = false) + void init () { release_spec (); @@ -1462,7 +1553,8 @@ public: m_is_iter = type_traits::is_iter (); mp_cls = type_traits::cls_decl (); - m_pass_obj = pass_obj; + m_pass_obj = compute_pass_obj::value (); + m_prefer_copy = compute_prefer_copy::value (); m_is_ref = type_traits::is_ref (); m_is_ptr = type_traits::is_ptr (); m_is_cref = type_traits::is_cref (); @@ -1481,12 +1573,56 @@ public: if (type_traits::inner_type>::code () != T_void) { mp_inner = new ArgType; - mp_inner->init::inner_type> (); + mp_inner->init::inner_type, arg_make_reference> (); } if (type_traits::inner_k_type>::code () != T_void) { mp_inner_k = new ArgType; - mp_inner_k->init::inner_k_type> (); + mp_inner_k->init::inner_k_type, arg_make_reference> (); + } + } + + /** + * @brief Initialize the type from a given type X + * If "pass_obj" is true, the receiver of an object will always + * take over the ownership over the passed object. This applies + * to the case of object pointers mainly. + */ + template + void init () + { + release_spec (); + + m_type = type_traits::code (); + m_is_iter = type_traits::is_iter (); + mp_cls = type_traits::cls_decl (); + + m_pass_obj = compute_pass_obj::value (); + m_prefer_copy = compute_prefer_copy::value (); + m_is_ref = type_traits::is_ref (); + m_is_ptr = type_traits::is_ptr (); + m_is_cref = type_traits::is_cref (); + m_is_cptr = type_traits::is_cptr (); + m_size = (unsigned int) type_traits::serial_size (); + + if (mp_inner) { + delete mp_inner; + mp_inner = 0; + } + + if (mp_inner_k) { + delete mp_inner_k; + mp_inner_k = 0; + } + + if (type_traits::inner_type>::code () != T_void) { + mp_inner = new ArgType; + mp_inner->init::inner_type, arg_make_reference> (); + } + + if (type_traits::inner_k_type>::code () != T_void) { + mp_inner_k = new ArgType; + mp_inner_k->init::inner_k_type, arg_make_reference> (); } } @@ -1571,6 +1707,22 @@ public: m_pass_obj = b; } + /** + * @brief Returns a value indicating that the value prefers to be copied + */ + bool prefer_copy () const + { + return m_prefer_copy; + } + + /** + * @brief Sets a value indicating that the value prefers to be copied + */ + void set_prefer_copy (bool b) + { + m_prefer_copy = b; + } + /** * @brief Returns a value indicating whether the type is a reference */ @@ -1702,6 +1854,7 @@ private: bool m_is_iter : 1; bool m_owns_spec : 1; bool m_pass_obj : 1; + bool m_prefer_copy : 1; mutable const ClassBase *mp_cls; unsigned int m_size; diff --git a/src/gsi/gsi_test/gsiTest.cc b/src/gsi/gsi_test/gsiTest.cc index 4750e7ad3..d115a54a7 100644 --- a/src/gsi/gsi_test/gsiTest.cc +++ b/src/gsi/gsi_test/gsiTest.cc @@ -1057,9 +1057,34 @@ static gsi::ClassExt b_ext ( gsi::iterator_ext ("b10p", &b10bp_ext, &b10ep_ext) ); +CopyDetector *new_cd (int x) +{ + return new CopyDetector (x); +} + +static gsi::Class decl_copy_detector ("", "CopyDetector", + gsi::constructor ("new", &new_cd) + + gsi::method ("x", &CopyDetector::x) + + gsi::method ("xx", &CopyDetector::xx) +); static gsi::Class decl_c ("", "C", gsi::callback ("f", &C_P::f, &C_P::f_cb) + + gsi::callback ("vfunc", &C_P::vfunc, &C_P::vfunc_cb) + + gsi::method ("call_vfunc", &C_P::call_vfunc) + + gsi::method ("pass_cd_direct", &C_P::pass_cd_direct) + + gsi::method ("pass_cd_cref", &C_P::pass_cd_cref) + + gsi::method ("pass_cd_cref_as_copy", &C_P::pass_cd_cref) + + gsi::method ("pass_cd_cref_as_ref", &C_P::pass_cd_cref) + + gsi::method ("pass_cd_cptr", &C_P::pass_cd_cptr) + + gsi::method ("pass_cd_cptr_as_copy", &C_P::pass_cd_cptr) + + gsi::method ("pass_cd_cptr_as_ref", &C_P::pass_cd_cptr) + + gsi::method ("pass_cd_ref", &C_P::pass_cd_ref) + + gsi::method ("pass_cd_ref_as_copy", &C_P::pass_cd_ref) + + gsi::method ("pass_cd_ref_as_ref", &C_P::pass_cd_ref) + + gsi::method ("pass_cd_ptr", &C_P::pass_cd_ptr) + + gsi::method ("pass_cd_ptr_as_copy", &C_P::pass_cd_ptr) + + gsi::method ("pass_cd_ptr_as_ref", &C_P::pass_cd_ptr) + gsi::method ("g", &C_P::g) + gsi::method ("s1", &C::s1) + gsi::method ("s2", &C::s2) + diff --git a/src/gsi/gsi_test/gsiTest.h b/src/gsi/gsi_test/gsiTest.h index 7992a42b3..a7e6938f6 100644 --- a/src/gsi/gsi_test/gsiTest.h +++ b/src/gsi/gsi_test/gsiTest.h @@ -846,6 +846,35 @@ struct B static B *b_inst; }; +class CopyDetector +{ +public: + CopyDetector (int x) + : m_x (x), m_xx (x) + { } + + CopyDetector () + : m_x (0), m_xx (0) + { } + + CopyDetector (const CopyDetector &d) + : m_x (d.m_x), m_xx (d.m_xx + 1) // this detects the copy + { } + + CopyDetector &operator= (const CopyDetector &d) + { + m_x = d.m_x; + m_xx = d.m_xx + 1; + return *this; + } + + int x () const { return m_x; } + int xx () const { return m_xx; } + +private: + int m_x, m_xx; +}; + class C { public: @@ -861,6 +890,22 @@ public: return f(s); } + virtual void vfunc (const CopyDetector &) + { + // .. nothing yet .. + } + + void call_vfunc (const CopyDetector &cd) + { + vfunc (cd); + } + + CopyDetector pass_cd_direct (const CopyDetector &cd) { return cd; } + const CopyDetector &pass_cd_cref (const CopyDetector &cd) { return cd; } + const CopyDetector *pass_cd_cptr (const CopyDetector &cd) { return &cd; } + CopyDetector *pass_cd_ptr (const CopyDetector &cd) { return const_cast (&cd); } + CopyDetector &pass_cd_ref (const CopyDetector &cd) { return const_cast (cd); } + static int s1 (); static std::vector::const_iterator s1a (); static std::vector::const_iterator s1b (); @@ -880,7 +925,13 @@ public: return f_cb.can_issue () ? f_cb.issue (&C::f, s) : C::f (s); } + virtual void vfunc (const CopyDetector &cd) + { + return vfunc_cb.can_issue () ? vfunc_cb.issue (&C::vfunc, cd) : C::vfunc (cd); + } + gsi::Callback f_cb; + gsi::Callback vfunc_cb; }; struct E diff --git a/src/pya/pya/pyaConvert.cc b/src/pya/pya/pyaConvert.cc index 4ceff8ecd..57918f95b 100644 --- a/src/pya/pya/pyaConvert.cc +++ b/src/pya/pya/pyaConvert.cc @@ -345,7 +345,7 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ArgType &atype) bool is_direct = !(atype.is_ptr () || atype.is_ref () || atype.is_cptr () || atype.is_cref ()); bool pass_obj = atype.pass_obj () || is_direct; bool is_const = atype.is_cptr () || atype.is_cref (); - bool prefer_copy = atype.is_cref (); + bool prefer_copy = atype.prefer_copy (); bool can_destroy = prefer_copy || atype.is_ptr (); return object_to_python (obj, self, cls, pass_obj, is_const, prefer_copy, can_destroy); diff --git a/src/rba/rba/rbaConvert.cc b/src/rba/rba/rbaConvert.cc index 1210624c5..e54bfb73b 100644 --- a/src/rba/rba/rbaConvert.cc +++ b/src/rba/rba/rbaConvert.cc @@ -133,7 +133,7 @@ VALUE object_to_ruby (void *obj, Proxy *self, const gsi::ArgType &atype) bool is_direct = !(atype.is_ptr () || atype.is_ref () || atype.is_cptr () || atype.is_cref ()); bool pass_obj = atype.pass_obj () || is_direct; bool is_const = atype.is_cptr () || atype.is_cref (); - bool prefer_copy = atype.is_cref (); + bool prefer_copy = atype.prefer_copy (); bool can_destroy = prefer_copy || atype.is_ptr (); return object_to_ruby (obj, self, cls, pass_obj, is_const, prefer_copy, can_destroy); diff --git a/testdata/python/basic.py b/testdata/python/basic.py index 568a9ee16..ec7f58ef0 100644 --- a/testdata/python/basic.py +++ b/testdata/python/basic.py @@ -73,6 +73,14 @@ class C_IMP2(pya.C): class C_IMP3(pya.C): anything = None +class C_IMP4(pya.C): + def __init__(self): + self.x = None + self.xx = None + def vfunc(self, cd): + self.x = cd.x() + self.xx = cd.xx() + class Z_IMP1(pya.Z): def f(self, x): return x.cls_name() @@ -1072,6 +1080,11 @@ class BasicTest(unittest.TestCase): arr.append(i) self.assertEqual( arr, [ 0, 1, 2, 3, 4, 5, 6, 0, 0, 1 ] ) + c4 = C_IMP4() + c4.call_vfunc(pya.CopyDetector(17)) + self.assertEqual(c4.x, 17) + self.assertEqual(c4.xx, 17) + def test_20(self): b = pya.B() @@ -1538,6 +1551,67 @@ class BasicTest(unittest.TestCase): b = None self.assertEqual(pya.B.has_inst(), False) + def test_29(self): + + # copy/ref semantics on return + + c = pya.C() + + cd = pya.CopyDetector(42) + + cd2 = c.pass_cd_direct(cd) + self.assertEqual(cd2.x(), 42) + # two copies: one for return statement and then one for the new object + self.assertEqual(cd2.xx(), 44) + + cd2 = c.pass_cd_cref(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 43) + + cd2 = c.pass_cd_cref_as_copy(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 43) + + cd2 = c.pass_cd_cref_as_ref(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 42) + + cd2 = c.pass_cd_cptr(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 42) + + cd2 = c.pass_cd_cptr_as_copy(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 43) + + cd2 = c.pass_cd_cptr_as_ref(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 42) + + cd2 = c.pass_cd_ptr(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 42) + + cd2 = c.pass_cd_ptr_as_copy(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 43) + + cd2 = c.pass_cd_ptr_as_ref(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 42) + + cd2 = c.pass_cd_ref(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 42) + + cd2 = c.pass_cd_ref_as_copy(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 43) + + cd2 = c.pass_cd_ref_as_ref(cd) + self.assertEqual(cd2.x(), 42) + self.assertEqual(cd2.xx(), 42) + def test_30(self): # some basic tests for the *Value boxing classes diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index 467ceaccf..8d9cd8493 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -936,6 +936,22 @@ class Basic_TestClass < TestBase class C_IMP3 < RBA::C end + class C_IMP4 < RBA::C + def initialize + @x = @xx = nil + end + def x + @x + end + def xx + @xx + end + def vfunc(cd) + @x = cd.x + @xx = cd.xx + end + end + def test_19 c0 = RBA::C.new @@ -978,6 +994,11 @@ class Basic_TestClass < TestBase C_IMP2.each { |i| arr.push i } assert_equal( arr, [ 0, 1, 2, 3, 4, 5, 6, 0, 0, 1 ] ) + c4 = C_IMP4.new + c4.call_vfunc(RBA::CopyDetector::new(17)) + assert_equal(c4.x, 17) + assert_equal(c4.xx, 17) + end def test_20 @@ -1414,6 +1435,69 @@ class Basic_TestClass < TestBase end + def test_29 + + # copy/ref semantics on return + + c = RBA::C::new + + cd = RBA::CopyDetector::new(42) + + cd2 = c.pass_cd_direct(cd) + assert_equal(cd2.x, 42) + # two copies: one for return statement and then one for the new object + assert_equal(cd2.xx, 44) + + cd2 = c.pass_cd_cref(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 43) + + cd2 = c.pass_cd_cref_as_copy(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 43) + + cd2 = c.pass_cd_cref_as_ref(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 42) + + cd2 = c.pass_cd_cptr(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 42) + + cd2 = c.pass_cd_cptr_as_copy(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 43) + + cd2 = c.pass_cd_cptr_as_ref(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 42) + + cd2 = c.pass_cd_ptr(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 42) + + cd2 = c.pass_cd_ptr_as_copy(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 43) + + cd2 = c.pass_cd_ptr_as_ref(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 42) + + cd2 = c.pass_cd_ref(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 42) + + cd2 = c.pass_cd_ref_as_copy(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 43) + + cd2 = c.pass_cd_ref_as_ref(cd) + assert_equal(cd2.x, 42) + assert_equal(cd2.xx, 42) + + end + def test_30 # some basic tests for the *Value boxing classes From 57305977a48e4bcc07ef2dda78556b7c216a29a5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 31 Jan 2019 22:23:58 +0100 Subject: [PATCH 226/335] Spice writer delegate fix - Changed to const & objects in the Spice writer delegate to non-const & for Ruby/Python reimplementation (as const/non-const ambiguity is an issue for Ruby/Python we cannot efficiently work with const refs) - Updated test data because the previous implementation wasn't using refs but rather copies of device and device class objects. --- src/db/db/gsiDeclDbNetlist.cc | 35 ++++++++++++++++++++++++------- testdata/algo/nwriter_rba2_au.txt | 20 +++++++++--------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index c572a3543..e3124665c 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1038,25 +1038,44 @@ public: } } - virtual void write_device_intro (const db::DeviceClass &cls) const + virtual void write_device_intro (const db::DeviceClass &ccls) const + { + reimpl_write_device_intro (const_cast (ccls)); + } + + // NOTE: we pass non-const refs to Ruby/Python - everthing else is a bit of a nightmare. + // Still that's not really clean. Just say, the implementation promises not to change the objects. + void reimpl_write_device_intro (db::DeviceClass &cls) const { if (cb_write_device_intro.can_issue ()) { - cb_write_device_intro.issue (&db::NetlistSpiceWriterDelegate::write_device_intro, cls); + cb_write_device_intro.issue (&NetlistSpiceWriterDelegateImpl::org_write_device_intro, const_cast (cls)); } else { - db::NetlistSpiceWriterDelegate::write_device_intro (cls); + org_write_device_intro (cls); } } - virtual void write_device (const db::Device &dev) const + void org_write_device_intro (db::DeviceClass &cls) const + { + db::NetlistSpiceWriterDelegate::write_device_intro (cls); + } + + virtual void write_device (const db::Device &cdev) const + { + reimpl_write_device (const_cast (cdev)); + } + + // NOTE: we pass non-const refs to Ruby/Python - everthing else is a bit of a nightmare. + // Still that's not really clean. Just say, the implementation promises not to change the objects. + void reimpl_write_device (db::Device &dev) const { if (cb_write_device.can_issue ()) { - cb_write_device.issue (&db::NetlistSpiceWriterDelegate::write_device, dev); + cb_write_device.issue (&NetlistSpiceWriterDelegateImpl::org_write_device, dev); } else { org_write_device (dev); } } - virtual void org_write_device (const db::Device &dev) const + void org_write_device (db::Device &dev) const { db::NetlistSpiceWriterDelegate::write_device (dev); } @@ -1076,11 +1095,11 @@ Class db_NetlistSpiceWriterDelegate ("db", "Netl "@brief Writes the text at the beginning of the SPICE netlist\n" "Reimplement this method to insert your own text at the beginning of the file" ) + - gsi::callback ("write_device_intro", &NetlistSpiceWriterDelegateImpl::write_device_intro, &NetlistSpiceWriterDelegateImpl::cb_write_device_intro, gsi::arg ("device_class"), + gsi::callback ("write_device_intro", &NetlistSpiceWriterDelegateImpl::reimpl_write_device_intro, &NetlistSpiceWriterDelegateImpl::cb_write_device_intro, gsi::arg ("device_class"), "@brief Inserts a text for the given device class\n" "Reimplement this method to insert your own text at the beginning of the file for the given device class" ) + - gsi::callback ("write_device", &NetlistSpiceWriterDelegateImpl::write_device, &NetlistSpiceWriterDelegateImpl::cb_write_device, gsi::arg ("device"), + gsi::callback ("write_device", &NetlistSpiceWriterDelegateImpl::reimpl_write_device, &NetlistSpiceWriterDelegateImpl::cb_write_device, gsi::arg ("device"), "@brief Inserts a text for the given device\n" "Reimplement this method to write the given device in the desired way" ) + diff --git a/testdata/algo/nwriter_rba2_au.txt b/testdata/algo/nwriter_rba2_au.txt index 6e4a8506b..492d0d858 100644 --- a/testdata/algo/nwriter_rba2_au.txt +++ b/testdata/algo/nwriter_rba2_au.txt @@ -15,15 +15,15 @@ * net 2 n2 * net 3 n3 * device instance $1 0,0 RCLS -* Before device $0 -* Terminal #1: 0 -* Terminal #2: 0 -R$0 0 0 1.7 -* After device $0 +* Before device $1 +* Terminal #1: 1 +* Terminal #2: 3 +R$1 1 3 1.7 +* After device $1 * device instance $2 0,0 RCLS -* Before device $0 -* Terminal #1: 0 -* Terminal #2: 0 -R$0 0 0 4.2e-05 -* After device $0 +* Before device $2 +* Terminal #1: 3 +* Terminal #2: 2 +R$2 3 2 4.2e-05 +* After device $2 .ENDS C1 From efe06046aa72c589540eb60fd978a282572566a6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 31 Jan 2019 23:50:34 +0100 Subject: [PATCH 227/335] Bugfixes (dbu, global nets) 1.) Fixed bug on build_nets when DBU's of target and source layout were different 2.) Global nets of subcircuits need to be considered also when they are already connected through other connections. --- src/db/db/dbHierNetworkProcessor.cc | 2 +- src/db/db/dbLayoutToNetlist.cc | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index d33a5631f..670d19851 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1892,7 +1892,7 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c const db::connected_clusters &cc = m_per_cell_clusters [inst->cell_index ()]; for (typename db::connected_clusters::const_iterator cl = cc.begin (); cl != cc.end (); ++cl) { - if (! cl->get_global_nets ().empty () && cc.is_root (cl->id ())) { + if (! cl->get_global_nets ().empty ()) { for (db::Instance::cell_inst_array_type::iterator i = inst->begin (); !i.at_end (); ++i) { global_net_clusters.add (cl->get_global_nets (), db::ClusterInstance (cl->id (), db::InstElement (*inst, i))); } diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index bd3a0244e..d0955b5ad 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -551,6 +551,11 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & return; } + // NOTE: we propagate the magnification part of tr down, but keep the rotation/translation part in the instance + // (we want to avoid magnified instances) + db::ICplxTrans tr_wo_mag = tr * db::ICplxTrans (1.0 / tr.mag ()); + db::ICplxTrans tr_mag (tr.mag ()); + const db::connected_clusters &clusters = m_net_clusters.clusters_per_cell (ci); typedef db::connected_clusters::connections_type connections_type; const connections_type &connections = clusters.connections_for_cluster (cid); @@ -576,7 +581,7 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & db::cell_index_type target_ci = target.add_cell ((std::string (name_prefix) + cell_name).c_str ()); cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), target_ci)).first; - build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, circuit_cell_name_prefix, device_cell_name_prefix, cmap, db::ICplxTrans ()); + build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, circuit_cell_name_prefix, device_cell_name_prefix, cmap, tr_mag); } else { cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), std::numeric_limits::max ())).first; @@ -585,7 +590,9 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & } if (cm->second != std::numeric_limits::max ()) { - target_cell->insert (db::CellInstArray (db::CellInst (cm->second), tr * c->inst ().complex_trans ())); + db::CellInstArray ci (db::CellInst (cm->second), tr_wo_mag * c->inst ().complex_trans ()); + ci.transform_into (tr_mag); + target_cell->insert (ci); } } @@ -600,7 +607,8 @@ LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &ta std::map, db::cell_index_type> cell_map; - build_net_rec (net, target, target_cell, lmap, 0, cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans ()); + double mag = internal_layout ()->dbu () / target.dbu (); + build_net_rec (net, target, target_cell, lmap, 0, cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag)); } void @@ -611,6 +619,7 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target } std::map, db::cell_index_type> cell_map; + double mag = internal_layout ()->dbu () / target.dbu (); const db::Netlist *netlist = mp_netlist.get (); for (db::Netlist::const_circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { @@ -630,7 +639,7 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target continue; } - build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans ()); + build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag)); } @@ -653,7 +662,7 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target if (n) { double dbu = target.dbu (); - db::ICplxTrans tr = db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu); + db::ICplxTrans tr = db::ICplxTrans (mag) * (db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu)); if (net_cell_name_prefix) { std::string ncn = std::string (net_cell_name_prefix) + subcircuit.expanded_name () + ":"; From f2fff5cca1e90fa085e3df0df3f6004fc114633c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 1 Feb 2019 00:00:10 +0100 Subject: [PATCH 228/335] Fixed compile issue. --- src/db/db/dbNetlistWriter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/dbNetlistWriter.h b/src/db/db/dbNetlistWriter.h index 2324f7454..a97f1fd2c 100644 --- a/src/db/db/dbNetlistWriter.h +++ b/src/db/db/dbNetlistWriter.h @@ -78,7 +78,7 @@ namespace tl { template <> -struct tl::type_traits +struct type_traits : public tl::type_traits { typedef tl::false_tag has_default_constructor; From 8ad6da0281303043026615d26e31f9dad83cc9f9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 1 Feb 2019 07:43:24 +0100 Subject: [PATCH 229/335] Fixed a build issue on MSVC --- src/tl/tl/tlList.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tl/tl/tlList.h b/src/tl/tl/tlList.h index 607fb27e7..0e35ed58b 100644 --- a/src/tl/tl/tlList.h +++ b/src/tl/tl/tlList.h @@ -44,7 +44,7 @@ class list_node public: list_node () : mp_next (0), mp_prev (0), m_owned (true) { } list_node (const list_node &) : mp_next (0), mp_prev (0), m_owned (true) { } - list_node &operator= (const list_node &) { } + list_node &operator= (const list_node &) { return *this; } ~list_node () { @@ -119,7 +119,7 @@ public: } list_impl (const list_impl &) { tl_assert (false); } - list_impl &operator= (const list_impl &) { tl_assert (false); } + list_impl &operator= (const list_impl &) { tl_assert (false); return *this; } ~list_impl () { From 11cfe36ed1df703cf222fa1d3afb92091ae24e2d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 1 Feb 2019 23:18:54 +0100 Subject: [PATCH 230/335] Some MSVC build issues fixed (hopefully) --- src/db/db/dbAsIfFlatRegion.cc | 2 +- src/db/db/dbDeepRegion.cc | 2 + src/db/db/dbDeepShapeStore.cc | 2 +- src/db/db/dbDeepShapeStore.h | 2 +- src/db/db/dbHierNetworkProcessor.cc | 11 +- src/db/db/dbHierNetworkProcessor.h | 2 +- src/db/db/dbLayoutToNetlistFormatDefs.cc | 122 +++++++++++++------ src/db/db/dbLayoutToNetlistFormatDefs.h | 30 +++-- src/db/db/dbNetlistDeviceExtractor.h | 1 - src/db/db/dbNetlistDeviceExtractorClasses.cc | 7 +- src/db/db/dbNetlistSpiceWriter.h | 1 - src/db/db/gsiDeclDbNetlist.cc | 5 - src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 14 +-- 13 files changed, 116 insertions(+), 85 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index c5b4c3b64..5e3ac458a 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -670,7 +670,7 @@ namespace /** * @brief A helper class to implement the strange polygon detector */ - struct StrangePolygonInsideFunc + struct DB_PUBLIC StrangePolygonInsideFunc { inline bool operator() (int wc) const { diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index f05396ff5..dd056b17c 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -52,6 +52,8 @@ namespace set (); } + virtual ~DeepRegionIterator () { } + virtual bool at_end () const { return m_iter.at_end (); diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index cf7c48bcf..d05ab60f8 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -390,7 +390,7 @@ DeepShapeStore::invalidate_hier () } const db::CellMapping & -DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set *excluded_cells) +DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set *excluded_cells) { const db::Layout *source_layout = &m_layouts [layout_index]->layout; if (source_layout->begin_top_down () == source_layout->end_top_cells ()) { diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 306b8f429..f8dac096b 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -242,7 +242,7 @@ public: * * "excluded_cells" - if not 0 - will exclude the given cells (and flatten them). */ - const db::CellMapping &cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set *excluded_cells = 0); + const db::CellMapping &cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set *excluded_cells = 0); /** * @brief For testing diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 670d19851..dd3c40e0f 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -301,17 +301,14 @@ local_cluster::ensure_sorted () m_needs_update = false; } -namespace -{ - template -struct interaction_receiver +struct DB_PUBLIC hnp_interaction_receiver : public box_scanner_receiver2 { public: typedef typename local_cluster::box_type box_type; - interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans) + hnp_interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans) : mp_conn (&conn), m_any (false), m_trans (trans) { // .. nothing yet .. @@ -357,8 +354,6 @@ private: Trans m_trans; }; -} - template typename local_cluster::shape_iterator local_cluster::begin (unsigned int l) const { @@ -464,7 +459,7 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans } } - interaction_receiver rec (conn, trans); + hnp_interaction_receiver rec (conn, trans); return ! scanner.process (rec, 1 /*==touching*/, bc, bc_t); } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index d34d0b7dd..d57db4211 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -330,7 +330,7 @@ public: private: template friend class local_clusters; - template friend class interaction_receiver; + template friend class hnp_interaction_receiver; void set_id (id_type id) { diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.cc b/src/db/db/dbLayoutToNetlistFormatDefs.cc index 572fdccb8..382c408bd 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.cc +++ b/src/db/db/dbLayoutToNetlistFormatDefs.cc @@ -27,47 +27,89 @@ namespace db namespace l2n_std_format { - template<> DB_PUBLIC const std::string keys::version_key ("version"); - template<> DB_PUBLIC const std::string keys::description_key ("description"); - template<> DB_PUBLIC const std::string keys::top_key ("top"); - template<> DB_PUBLIC const std::string keys::unit_key ("unit"); - template<> DB_PUBLIC const std::string keys::layer_key ("layer"); - template<> DB_PUBLIC const std::string keys::connect_key ("connect"); - template<> DB_PUBLIC const std::string keys::global_key ("global"); - template<> DB_PUBLIC const std::string keys::circuit_key ("circuit"); - template<> DB_PUBLIC const std::string keys::net_key ("net"); - template<> DB_PUBLIC const std::string keys::device_key ("device"); - template<> DB_PUBLIC const std::string keys::polygon_key ("polygon"); - template<> DB_PUBLIC const std::string keys::rect_key ("rect"); - template<> DB_PUBLIC const std::string keys::terminal_key ("terminal"); - template<> DB_PUBLIC const std::string keys::abstract_key ("abstract"); - template<> DB_PUBLIC const std::string keys::param_key ("param"); - template<> DB_PUBLIC const std::string keys::location_key ("location"); - template<> DB_PUBLIC const std::string keys::rotation_key ("rotation"); - template<> DB_PUBLIC const std::string keys::mirror_key ("mirror"); - template<> DB_PUBLIC const std::string keys::scale_key ("scale"); - template<> DB_PUBLIC const std::string keys::pin_key ("pin"); + static const std::string long_version_key ("version"); + static const std::string long_description_key ("description"); + static const std::string long_top_key ("top"); + static const std::string long_unit_key ("unit"); + static const std::string long_layer_key ("layer"); + static const std::string long_connect_key ("connect"); + static const std::string long_global_key ("global"); + static const std::string long_circuit_key ("circuit"); + static const std::string long_net_key ("net"); + static const std::string long_device_key ("device"); + static const std::string long_polygon_key ("polygon"); + static const std::string long_rect_key ("rect"); + static const std::string long_terminal_key ("terminal"); + static const std::string long_abstract_key ("abstract"); + static const std::string long_param_key ("param"); + static const std::string long_location_key ("location"); + static const std::string long_rotation_key ("rotation"); + static const std::string long_mirror_key ("mirror"); + static const std::string long_scale_key ("scale"); + static const std::string long_pin_key ("pin"); - template<> DB_PUBLIC const std::string keys::version_key ("V"); - template<> DB_PUBLIC const std::string keys::description_key ("B"); - template<> DB_PUBLIC const std::string keys::top_key ("W"); - template<> DB_PUBLIC const std::string keys::unit_key ("U"); - template<> DB_PUBLIC const std::string keys::layer_key ("L"); - template<> DB_PUBLIC const std::string keys::connect_key ("C"); - template<> DB_PUBLIC const std::string keys::global_key ("G"); - template<> DB_PUBLIC const std::string keys::circuit_key ("X"); - template<> DB_PUBLIC const std::string keys::net_key ("N"); - template<> DB_PUBLIC const std::string keys::device_key ("D"); - template<> DB_PUBLIC const std::string keys::polygon_key ("Q"); - template<> DB_PUBLIC const std::string keys::rect_key ("R"); - template<> DB_PUBLIC const std::string keys::terminal_key ("T"); - template<> DB_PUBLIC const std::string keys::abstract_key ("A"); - template<> DB_PUBLIC const std::string keys::param_key ("E"); - template<> DB_PUBLIC const std::string keys::location_key ("Y"); - template<> DB_PUBLIC const std::string keys::rotation_key ("O"); - template<> DB_PUBLIC const std::string keys::mirror_key ("M"); - template<> DB_PUBLIC const std::string keys::scale_key ("S"); - template<> DB_PUBLIC const std::string keys::pin_key ("P"); + static const std::string short_version_key ("V"); + static const std::string short_description_key ("B"); + static const std::string short_top_key ("W"); + static const std::string short_unit_key ("U"); + static const std::string short_layer_key ("L"); + static const std::string short_connect_key ("C"); + static const std::string short_global_key ("G"); + static const std::string short_circuit_key ("X"); + static const std::string short_net_key ("N"); + static const std::string short_device_key ("D"); + static const std::string short_polygon_key ("Q"); + static const std::string short_rect_key ("R"); + static const std::string short_terminal_key ("T"); + static const std::string short_abstract_key ("A"); + static const std::string short_param_key ("E"); + static const std::string short_location_key ("Y"); + static const std::string short_rotation_key ("O"); + static const std::string short_mirror_key ("M"); + static const std::string short_scale_key ("S"); + static const std::string short_pin_key ("P"); + + template<> const std::string &keys::version_key = long_version_key; + template<> const std::string &keys::description_key = long_description_key; + template<> const std::string &keys::top_key = long_top_key; + template<> const std::string &keys::unit_key = long_unit_key; + template<> const std::string &keys::layer_key = long_layer_key; + template<> const std::string &keys::connect_key = long_connect_key; + template<> const std::string &keys::global_key = long_global_key; + template<> const std::string &keys::circuit_key = long_circuit_key; + template<> const std::string &keys::net_key = long_net_key; + template<> const std::string &keys::device_key = long_device_key; + template<> const std::string &keys::polygon_key = long_polygon_key; + template<> const std::string &keys::rect_key = long_rect_key; + template<> const std::string &keys::terminal_key = long_terminal_key; + template<> const std::string &keys::abstract_key = long_abstract_key; + template<> const std::string &keys::param_key = long_param_key; + template<> const std::string &keys::location_key = long_location_key; + template<> const std::string &keys::rotation_key = long_rotation_key; + template<> const std::string &keys::mirror_key = long_mirror_key; + template<> const std::string &keys::scale_key = long_scale_key; + template<> const std::string &keys::pin_key = long_pin_key; + + template<> const std::string &keys::version_key = short_version_key; + template<> const std::string &keys::description_key = short_description_key; + template<> const std::string &keys::top_key = short_top_key; + template<> const std::string &keys::unit_key = short_unit_key; + template<> const std::string &keys::layer_key = short_layer_key; + template<> const std::string &keys::connect_key = short_connect_key; + template<> const std::string &keys::global_key = short_global_key; + template<> const std::string &keys::circuit_key = short_circuit_key; + template<> const std::string &keys::net_key = short_net_key; + template<> const std::string &keys::device_key = short_device_key; + template<> const std::string &keys::polygon_key = short_polygon_key; + template<> const std::string &keys::rect_key = short_rect_key; + template<> const std::string &keys::terminal_key = short_terminal_key; + template<> const std::string &keys::abstract_key = short_abstract_key; + template<> const std::string &keys::param_key = short_param_key; + template<> const std::string &keys::location_key = short_location_key; + template<> const std::string &keys::rotation_key = short_rotation_key; + template<> const std::string &keys::mirror_key = short_mirror_key; + template<> const std::string &keys::scale_key = short_scale_key; + template<> const std::string &keys::pin_key = short_pin_key; } } diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index fd6793473..ed57f2d03 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -103,13 +103,29 @@ namespace l2n_std_format template struct DB_PUBLIC keys { - static const std::string version_key, description_key, top_key, unit_key, - layer_key, connect_key, global_key, - circuit_key, net_key, device_key, subcircuit_key, - polygon_key, rect_key, terminal_key, abstract_key, - param_key, location_key, rotation_key, - mirror_key, scale_key, pin_key, - indent1, indent2; + static const std::string &version_key, + &description_key, + &top_key, + &unit_key, + &layer_key, + &connect_key, + &global_key, + &circuit_key, + &net_key, + &device_key, + &subcircuit_key, + &polygon_key, + &rect_key, + &terminal_key, + &abstract_key, + ¶m_key, + &location_key, + &rotation_key, + &mirror_key, + &scale_key, + &pin_key, + &indent1, + &indent2; inline static bool is_short () { return Short; } }; diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index c82e189d4..db7fca2e3 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -308,7 +308,6 @@ public: return m_layer_definitions.end (); } -protected: /** * @brief Sets the name of the device class and the device extractor */ diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index a82fa11ff..15531fea8 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -108,12 +108,7 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vectorbox().to_string().c_str()); - printf("@@@ rdiff=%s\n", rdiff.to_string().c_str()); - printf("@@@ rgates=%s\n", rgates.to_string().c_str()); fflush(stdout); - } + size_t n = rgates.selected_interacting (db::Region (*d)).size (); tl_assert (n > 0); device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, dbu () * dbu () * d->area () / double (n)); diff --git a/src/db/db/dbNetlistSpiceWriter.h b/src/db/db/dbNetlistSpiceWriter.h index d8dd294d3..2a5dfb418 100644 --- a/src/db/db/dbNetlistSpiceWriter.h +++ b/src/db/db/dbNetlistSpiceWriter.h @@ -57,7 +57,6 @@ public: virtual void write_device_intro (const db::DeviceClass &cls) const; virtual void write_device (const db::Device &dev) const; -protected: std::string net_to_string (const db::Net *net) const; void emit_line (const std::string &line) const; void emit_comment (const std::string &comment) const; diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index e3124665c..0b5b4d0d8 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1083,11 +1083,6 @@ public: gsi::Callback cb_write_header; gsi::Callback cb_write_device_intro; gsi::Callback cb_write_device; - - using db::NetlistSpiceWriterDelegate::emit_comment; - using db::NetlistSpiceWriterDelegate::emit_line; - using db::NetlistSpiceWriterDelegate::net_to_string; - using db::NetlistSpiceWriterDelegate::format_name; }; Class db_NetlistSpiceWriterDelegate ("db", "NetlistSpiceWriterDelegate", diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index 7ccacac19..29c04a5eb 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -39,18 +39,6 @@ public: // .. nothing yet .. } - using db::NetlistDeviceExtractor::set_name; - using db::NetlistDeviceExtractor::define_layer; - using db::NetlistDeviceExtractor::define_terminal; - using db::NetlistDeviceExtractor::create_device; - using db::NetlistDeviceExtractor::dbu; - using db::NetlistDeviceExtractor::layout; - using db::NetlistDeviceExtractor::cell_index; - using db::NetlistDeviceExtractor::cell_name; - using db::NetlistDeviceExtractor::error; - using db::NetlistDeviceExtractor::get_connectivity; - using db::NetlistDeviceExtractor::extract_devices; - void register_device_class (db::DeviceClass *device_class) { // the class is owned by the extractor @@ -190,7 +178,7 @@ static const std::string &ld_description (const db::NetlistDeviceExtractorLayerD return ld->description; } -static int ld_index (const db::NetlistDeviceExtractorLayerDefinition *ld) +static size_t ld_index (const db::NetlistDeviceExtractorLayerDefinition *ld) { return ld->index; } From c90f7e4af9908bdaf2f9922b556d883e37cdaeef Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 Feb 2019 01:29:28 +0100 Subject: [PATCH 231/335] Introduced perimeter parameters for MOS3/MOS4 --- src/db/db/dbNetlistDeviceClasses.cc | 23 ++-- src/db/db/dbNetlistDeviceClasses.h | 9 +- src/db/db/dbNetlistDeviceExtractor.cc | 2 +- src/db/db/dbNetlistDeviceExtractor.h | 2 +- src/db/db/dbNetlistDeviceExtractorClasses.cc | 1 + src/db/db/dbNetlistSpiceWriter.cc | 2 + src/db/db/gsiDeclDbNetlistDeviceClasses.cc | 20 +++- .../unit_tests/dbHierNetworkProcessorTests.cc | 6 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 76 ++++++------- .../unit_tests/dbNetlistDeviceClassesTests.cc | 100 ++++++++++++------ src/db/unit_tests/dbNetlistExtractorTests.cc | 96 ++++++++--------- src/db/unit_tests/dbNetlistWriterTests.cc | 12 +++ testdata/algo/l2n_writer_au.txt | 8 ++ testdata/algo/l2n_writer_au_2.txt | 8 ++ testdata/algo/l2n_writer_au_2s.txt | 8 ++ testdata/algo/l2n_writer_au_s.txt | 8 ++ testdata/algo/nwriter5_au.txt | 4 +- testdata/algo/nwriter6_au.txt | 4 +- testdata/algo/nwriter8_au.txt | 4 +- testdata/ruby/dbLayoutToNetlist.rb | 24 ++--- testdata/ruby/dbNetlistDeviceClasses.rb | 20 ++-- 21 files changed, 278 insertions(+), 159 deletions(-) diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index 393653d05..05a1a59c8 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -199,6 +199,8 @@ DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_L = 0; DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_W = 1; DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_AS = 2; DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_AD = 3; +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_PS = 4; +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_PD = 5; DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_S = 0; DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_G = 1; @@ -214,6 +216,8 @@ DeviceClassMOS3Transistor::DeviceClassMOS3Transistor () add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0)); add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0)); add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0)); } bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const @@ -231,9 +235,8 @@ bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const // for combination the gate length must be identical if (fabs (a->parameter_value (0) - b->parameter_value (0)) < 1e-6) { - a->set_parameter_value (1, a->parameter_value (1) + b->parameter_value (1)); - a->set_parameter_value (2, a->parameter_value (2) + b->parameter_value (2)); - a->set_parameter_value (3, a->parameter_value (3) + b->parameter_value (3)); + combine_parameters (a, b); + b->connect_terminal (0, 0); b->connect_terminal (1, 0); b->connect_terminal (2, 0); @@ -247,6 +250,15 @@ bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const return false; } +void DeviceClassMOS3Transistor::combine_parameters (Device *a, Device *b) const +{ + a->set_parameter_value (1, a->parameter_value (1) + b->parameter_value (1)); + a->set_parameter_value (2, a->parameter_value (2) + b->parameter_value (2)); + a->set_parameter_value (3, a->parameter_value (3) + b->parameter_value (3)); + a->set_parameter_value (4, a->parameter_value (4) + b->parameter_value (4)); + a->set_parameter_value (5, a->parameter_value (5) + b->parameter_value (5)); +} + // ------------------------------------------------------------------------------------ // DeviceClassMOS4Transistor implementation @@ -274,9 +286,8 @@ bool DeviceClassMOS4Transistor::combine_devices (Device *a, Device *b) const // for combination the gate length must be identical if (fabs (a->parameter_value (0) - b->parameter_value (0)) < 1e-6) { - a->set_parameter_value (1, a->parameter_value (1) + b->parameter_value (1)); - a->set_parameter_value (2, a->parameter_value (2) + b->parameter_value (2)); - a->set_parameter_value (3, a->parameter_value (3) + b->parameter_value (3)); + combine_parameters (a, b); + b->connect_terminal (0, 0); b->connect_terminal (1, 0); b->connect_terminal (2, 0); diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index fca6d3c15..c7d4b5187 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -147,8 +147,8 @@ public: /** * @brief A basic MOSFET device class with three terminals - * A MOSFET defines four parameters: "W" for the gate width in micrometers, "L" for the gate length in micrometers, - * "AS" for the source area and "AD" for the drain area. + * A MOSFET defines six parameters: "W" for the gate width in micrometers, "L" for the gate length in micrometers, + * "AS" for the source area and "AD" for the drain area and "PS" and "PD" for the source and drain perimeter. * The MOSFET device defines three terminals, "S", "D" and "G" for source, drain and gate. */ class DB_PUBLIC DeviceClassMOS3Transistor @@ -161,6 +161,8 @@ public: static size_t param_id_W; static size_t param_id_AS; static size_t param_id_AD; + static size_t param_id_PS; + static size_t param_id_PD; static size_t terminal_id_S; static size_t terminal_id_G; @@ -173,6 +175,9 @@ public: virtual bool combine_devices (Device *a, Device *b) const; virtual bool supports_parallel_combination () const { return true; } + +protected: + void combine_parameters (Device *a, Device *b) const; }; /** diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 3edbf16bd..d30906149 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -327,7 +327,7 @@ void NetlistDeviceExtractor::push_new_devices (const db::Vector &disp_cache) DeviceCellKey key; for (geometry_per_terminal_type::const_iterator t = d->second.second.begin (); t != d->second.second.end (); ++t) { - std::map > > = key.geometry [t->first]; + std::map > > = key.geometry [t->first]; for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) { std::set &gl = gt [l->first]; for (std::vector::const_iterator p = l->second.begin (); p != l->second.end (); ++p) { diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index db7fca2e3..12a4ce280 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -493,7 +493,7 @@ private: return false; } - std::map > > geometry; + std::map > > geometry; std::map parameters; }; diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index 15531fea8..8fa72d3ac 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -112,6 +112,7 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector 0); device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, dbu () * dbu () * d->area () / double (n)); + device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_PS : db::DeviceClassMOS3Transistor::param_id_PD, dbu () * d->perimeter () / double (n)); define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, terminal_geometry_index, *d); diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc index 338edce2b..833cd1af5 100644 --- a/src/db/db/dbNetlistSpiceWriter.cc +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -149,6 +149,8 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const os << " W=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_W)); os << " AS=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AS)); os << " AD=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AD)); + os << " PS=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PS)); + os << " PD=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PD)); } else { diff --git a/src/db/db/gsiDeclDbNetlistDeviceClasses.cc b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc index 8d1fc2ac4..4bd11a670 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceClasses.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc @@ -127,14 +127,20 @@ Class decl_dbDeviceClassMOS3Transistor (decl_dbDe ) + gsi::constant ("PARAM_AD", db::DeviceClassMOS3Transistor::param_id_AD, "@brief A constant giving the parameter ID for parameter AD" + ) + + gsi::constant ("PARAM_PS", db::DeviceClassMOS3Transistor::param_id_PS, + "@brief A constant giving the parameter ID for parameter PS" + ) + + gsi::constant ("PARAM_PD", db::DeviceClassMOS3Transistor::param_id_PD, + "@brief A constant giving the parameter ID for parameter PD" ), "@brief A device class for a 3-terminal MOS transistor.\n" "This class can be used to describe MOS transistors without a bulk terminal. " "A device class for a MOS transistor with a bulk terminal is \\DeviceClassMOS4Transistor. " "MOS transistors are defined by their combination behavior and the basic parameters.\n" "\n" - "The parameters are L, W, AS and AD for the gate length and width in micrometers and source and drain area " - "in square micrometers.\n" + "The parameters are L, W, AS, AD, PS and PD for the gate length and width in micrometers, source and drain area " + "in square micrometers and the source and drain perimeter in micrometers.\n" "\n" "The terminals are S, G and D for source, gate and drain.\n" "\n" @@ -169,14 +175,20 @@ Class decl_dbDeviceClassMOS4Transistor (decl_dbDe ) + gsi::constant ("PARAM_AD", db::DeviceClassMOS4Transistor::param_id_AD, "@brief A constant giving the parameter ID for parameter AD" + ) + + gsi::constant ("PARAM_PS", db::DeviceClassMOS4Transistor::param_id_PS, + "@brief A constant giving the parameter ID for parameter PS" + ) + + gsi::constant ("PARAM_PD", db::DeviceClassMOS4Transistor::param_id_PD, + "@brief A constant giving the parameter ID for parameter PD" ), "@brief A device class for a 4-terminal MOS transistor.\n" "This class can be used to describe MOS transistors with a bulk terminal. " "A device class for a MOS transistor without a bulk terminal is \\DeviceClassMOS3Transistor. " "MOS transistors are defined by their combination behavior and the basic parameters.\n" "\n" - "The parameters are L, W, AS and AD for the gate length and width in micrometers and source and drain area " - "in square micrometers.\n" + "The parameters are L, W, AS, AD, PS and PD for the gate length and width in micrometers, source and drain area " + "in square micrometers and the source and drain perimeter in micrometers.\n" "\n" "The terminals are S, G, D and B for source, gate, drain and bulk.\n" "\n" diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index c81cc404b..ba5801b4e 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -659,7 +659,7 @@ TEST(40_HierClustersBasic) EXPECT_EQ (cluster->bbox ().to_string (), "(0,0;1000,1000)") nc = 0; for (db::connected_clusters::connections_iterator i = cluster->begin_connections (); i != cluster->end_connections (); ++i) { - nc += i->second.size (); + nc += int (i->second.size ()); } EXPECT_EQ (nc, 2); @@ -673,7 +673,7 @@ TEST(40_HierClustersBasic) EXPECT_EQ (cluster->bbox ().to_string (), "(0,0;2000,500)") nc = 0; for (db::connected_clusters::connections_iterator i = cluster->begin_connections (); i != cluster->end_connections (); ++i) { - nc += i->second.size (); + nc += int (i->second.size ()); } EXPECT_EQ (nc, 0); @@ -687,7 +687,7 @@ TEST(40_HierClustersBasic) EXPECT_EQ (cluster->bbox ().to_string (), "(0,0;500,2000)") nc = 0; for (db::connected_clusters::connections_iterator i = cluster->begin_connections (); i != cluster->end_connections (); ++i) { - nc += i->second.size (); + nc += int (i->second.size ()); } EXPECT_EQ (nc, 1); } diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index e063dab20..c28e53f25 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -350,10 +350,10 @@ TEST(1_BasicExtraction) " XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)\n" " XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)\n" "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" @@ -505,10 +505,10 @@ TEST(1_BasicExtraction) " XINV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD)\n" " XINV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD)\n" "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" ); // do some probing after purging @@ -695,10 +695,10 @@ TEST(2_Probing) " XINV2 $1 (IN=$I3,$2=$I7,OUT=$I1,$4=$I4,$5=$I5)\n" " XINV2 $2 (IN=$I2,$2=$I6,OUT=$I3,$4=$I4,$5=$I5)\n" "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" @@ -747,10 +747,10 @@ TEST(2_Probing) " XINV2 $1 (IN=$I3,$2=$I7,OUT=$I1,$4=$I4,$5=$I5)\n" " XINV2 $2 (IN=$I2,$2=(null),OUT=$I3,$4=$I4,$5=$I5)\n" "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" ); // do some probing after purging @@ -967,10 +967,10 @@ TEST(3_GlobalNetConnections) " XINV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" - " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" " XTRANS $1 ($1=$3,$2=VSS,$3=IN)\n" " XTRANS $2 ($1=$3,$2=VDD,$3=IN)\n" " XTRANS $3 ($1=VDD,$2=OUT,$3=$3)\n" @@ -1019,10 +1019,10 @@ TEST(3_GlobalNetConnections) " XINV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" "Circuit INV2 ($1=(null),IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=(null)):\n" - " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" ); // do some probing after purging @@ -1245,10 +1245,10 @@ TEST(4_GlobalNetDeviceExtraction) " XINV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" - " DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" " XTRANS $1 ($1=$3,$2=VSS,$3=IN)\n" " XTRANS $2 ($1=$3,$2=VDD,$3=IN)\n" " XTRANS $3 ($1=VDD,$2=OUT,$3=$3)\n" @@ -1297,10 +1297,10 @@ TEST(4_GlobalNetDeviceExtraction) " XINV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" - " DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" ); // do some probing after purging @@ -1523,10 +1523,10 @@ TEST(5_DeviceExtractionWithDeviceCombination) " XINV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" " XINV2 $2 ($1=$I1,IN=$I4,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" "Circuit INV2 ($1=$1,IN=IN,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" - " DPMOS $1 (S=OUT,G=IN,D=VDD,B=$1) [L=0.25,W=1.75,AS=0.91875,AD=0.48125]\n" - " DPMOS $2 (S=VDD,G=IN,D=OUT,B=$1) [L=0.25,W=1.75,AS=0.48125,AD=0.91875]\n" - " DNMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) [L=0.25,W=1.75,AS=0.91875,AD=0.48125]\n" - " DNMOS $4 (S=VSS,G=IN,D=OUT,B=BULK) [L=0.25,W=1.75,AS=0.48125,AD=0.91875]\n" + " DPMOS $1 (S=OUT,G=IN,D=VDD,B=$1) [L=0.25,W=1.75,AS=0.91875,AD=0.48125,PS=4.55,PD=2.3]\n" + " DPMOS $2 (S=VDD,G=IN,D=OUT,B=$1) [L=0.25,W=1.75,AS=0.48125,AD=0.91875,PS=2.3,PD=4.55]\n" + " DNMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) [L=0.25,W=1.75,AS=0.91875,AD=0.48125,PS=4.55,PD=2.3]\n" + " DNMOS $4 (S=VSS,G=IN,D=OUT,B=BULK) [L=0.25,W=1.75,AS=0.48125,AD=0.91875,PS=2.3,PD=4.55]\n" " XTRANS $1 ($1=OUT,$2=VSS,$3=IN)\n" " XTRANS $2 ($1=OUT,$2=VDD,$3=IN)\n" " XTRANS $3 ($1=OUT,$2=VSS,$3=IN)\n" @@ -1572,8 +1572,8 @@ TEST(5_DeviceExtractionWithDeviceCombination) " XINV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" " XINV2 $2 ($1=$I1,IN=$I4,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" "Circuit INV2 ($1=$1,IN=IN,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" - " DPMOS $1 (S=OUT,G=IN,D=VDD,B=$1) [L=0.25,W=3.5,AS=1.4,AD=1.4]\n" - " DNMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) [L=0.25,W=3.5,AS=1.4,AD=1.4]\n" + " DPMOS $1 (S=OUT,G=IN,D=VDD,B=$1) [L=0.25,W=3.5,AS=1.4,AD=1.4,PS=6.85,PD=6.85]\n" + " DNMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) [L=0.25,W=3.5,AS=1.4,AD=1.4,PS=6.85,PD=6.85]\n" ); // do some probing after purging diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc index f37a5e397..095541605 100644 --- a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -916,11 +916,15 @@ TEST(20_ParallelMOS3Transistors) d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); db::Device *d2 = new db::Device (cls, "d2"); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); @@ -952,8 +956,8 @@ TEST(20_ParallelMOS3Transistors) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n3,D=n2) [L=0.5,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); nl.combine_devices (); @@ -961,7 +965,7 @@ TEST(20_ParallelMOS3Transistors) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7]\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" ); } @@ -977,11 +981,15 @@ TEST(21_AntiParallelMOS3Transistors) d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); db::Device *d2 = new db::Device (cls, "d2"); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); @@ -1013,8 +1021,8 @@ TEST(21_AntiParallelMOS3Transistors) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n2,G=n3,D=n1) [L=0.5,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n2,G=n3,D=n1) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); nl.combine_devices (); @@ -1022,7 +1030,7 @@ TEST(21_AntiParallelMOS3Transistors) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7]\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" ); } @@ -1038,11 +1046,15 @@ TEST(22_ParallelMOS3TransistorsDisconnectedGates) d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); db::Device *d2 = new db::Device (cls, "d2"); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); @@ -1079,8 +1091,8 @@ TEST(22_ParallelMOS3TransistorsDisconnectedGates) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C1=n3,C2=n4):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); nl.combine_devices (); @@ -1090,8 +1102,8 @@ TEST(22_ParallelMOS3TransistorsDisconnectedGates) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C1=n3,C2=n4):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); } @@ -1107,11 +1119,15 @@ TEST(23_ParallelMOS3TransistorsDifferentLength) d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); db::Device *d2 = new db::Device (cls, "d2"); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.75); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); @@ -1143,8 +1159,8 @@ TEST(23_ParallelMOS3TransistorsDifferentLength) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); nl.combine_devices (); @@ -1154,8 +1170,8 @@ TEST(23_ParallelMOS3TransistorsDifferentLength) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); } @@ -1171,11 +1187,15 @@ TEST(30_ParallelMOS4Transistors) d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); db::Device *d2 = new db::Device (cls, "d2"); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); @@ -1214,8 +1234,8 @@ TEST(30_ParallelMOS4Transistors) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); nl.combine_devices (); @@ -1223,7 +1243,7 @@ TEST(30_ParallelMOS4Transistors) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7]\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" ); } @@ -1239,11 +1259,15 @@ TEST(31_AntiParallelMOS4Transistors) d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); db::Device *d2 = new db::Device (cls, "d2"); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); @@ -1282,8 +1306,8 @@ TEST(31_AntiParallelMOS4Transistors) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n2,G=n3,D=n1,B=n0) [L=0.5,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n2,G=n3,D=n1,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); nl.combine_devices (); @@ -1291,7 +1315,7 @@ TEST(31_AntiParallelMOS4Transistors) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7]\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" ); } @@ -1307,11 +1331,15 @@ TEST(32_ParallelMOS4TransistorsDisconnectedGates) d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); db::Device *d2 = new db::Device (cls, "d2"); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); @@ -1355,8 +1383,8 @@ TEST(32_ParallelMOS4TransistorsDisconnectedGates) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C1=n3a,C2=n3b,D=n0):\n" - " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); nl.combine_devices (); @@ -1366,8 +1394,8 @@ TEST(32_ParallelMOS4TransistorsDisconnectedGates) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C1=n3a,C2=n3b,D=n0):\n" - " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); } @@ -1383,11 +1411,15 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); db::Device *d2 = new db::Device (cls, "d2"); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); @@ -1431,8 +1463,8 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3,D1=n0a,D2=n0b):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); // not combined because bulk is different: @@ -1442,8 +1474,8 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3,D1=n0a,D2=n0b):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); } @@ -1459,11 +1491,15 @@ TEST(34_ParallelMOS4TransistorsDifferentLength) d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); db::Device *d2 = new db::Device (cls, "d2"); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.75); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); db::Circuit *circuit = new db::Circuit (); nl.add_circuit (circuit); @@ -1502,8 +1538,8 @@ TEST(34_ParallelMOS4TransistorsDifferentLength) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); nl.combine_devices (); @@ -1513,8 +1549,8 @@ TEST(34_ParallelMOS4TransistorsDifferentLength) EXPECT_EQ (nl.to_string (), "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3]\n" - " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4]\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" ); } diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 22d590b75..aaaa44a27 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -293,10 +293,10 @@ TEST(1_DeviceAndNetExtraction) " XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)\n" " XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)\n" "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" @@ -325,10 +325,10 @@ TEST(1_DeviceAndNetExtraction) " XINV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD)\n" " XINV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD)\n" "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" ); // compare the collected test data @@ -496,46 +496,46 @@ TEST(2_DeviceAndNetExtractionFlat) // happen to be the same because they share the same label. EXPECT_EQ (nl.to_string (), "Circuit RINGO ():\n" - " DPMOS $1 (S=$16,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $2 (S=VDD,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $3 (S=$14,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $4 (S=VDD,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $5 (S=$12,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $6 (S=VDD,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $9 (S=$4,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $10 (S=VDD,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $11 (S=$8,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $12 (S=VDD,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $13 (S=$2,G='IN,FB',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $14 (S=VDD,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $15 (S=$6,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $16 (S=VDD,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $17 (S=$18,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $18 (S=VDD,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DPMOS $19 (S=$10,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DPMOS $20 (S=VDD,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $23 (S=$18,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $24 (S=VSS,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $25 (S=$14,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $26 (S=VSS,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $27 (S=$12,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $28 (S=VSS,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $29 (S=$4,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $30 (S=VSS,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $31 (S=$2,G='IN,FB',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $32 (S=VSS,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $33 (S=$8,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $34 (S=VSS,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $35 (S=$6,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $36 (S=VSS,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $37 (S=$16,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $38 (S=VSS,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" - " DNMOS $39 (S=$10,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" - " DNMOS $40 (S=VSS,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DPMOS $1 (S=$16,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=VDD,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $3 (S=$14,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $4 (S=VDD,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $5 (S=$12,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $6 (S=VDD,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $9 (S=$4,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $10 (S=VDD,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $11 (S=$8,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $12 (S=VDD,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $13 (S=$2,G='IN,FB',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $14 (S=VDD,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $15 (S=$6,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $16 (S=VDD,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $17 (S=$18,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $18 (S=VDD,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $19 (S=$10,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $20 (S=VDD,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $23 (S=$18,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $24 (S=VSS,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $25 (S=$14,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $26 (S=VSS,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $27 (S=$12,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $28 (S=VSS,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $29 (S=$4,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $30 (S=VSS,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $31 (S=$2,G='IN,FB',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $32 (S=VSS,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $33 (S=$8,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $34 (S=VSS,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $35 (S=$6,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $36 (S=VSS,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $37 (S=$16,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $38 (S=VSS,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $39 (S=$10,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $40 (S=VSS,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" ); // compare the collected test data diff --git a/src/db/unit_tests/dbNetlistWriterTests.cc b/src/db/unit_tests/dbNetlistWriterTests.cc index 24ec3f413..2132169a4 100644 --- a/src/db/unit_tests/dbNetlistWriterTests.cc +++ b/src/db/unit_tests/dbNetlistWriterTests.cc @@ -393,11 +393,15 @@ TEST(5_WriterMOS3Devices) ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18); ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2); ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.75); db::Device *ddev2 = new db::Device (m3cls); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.85); circuit1->add_device (ddev1); circuit1->add_device (ddev2); @@ -488,11 +492,15 @@ TEST(6_WriterMOS4Devices) ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18); ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2); ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.75); db::Device *ddev2 = new db::Device (m4cls); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.85); circuit1->add_device (ddev1); circuit1->add_device (ddev2); @@ -657,11 +665,15 @@ TEST(8_WriterSubcircuits) ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18); ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2); ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.75); db::Device *ddev2 = new db::Device (m4cls); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3); ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.85); circuit1->add_device (ddev1); circuit1->add_device (ddev2); diff --git a/testdata/algo/l2n_writer_au.txt b/testdata/algo/l2n_writer_au.txt index 2861391d6..03320e97f 100644 --- a/testdata/algo/l2n_writer_au.txt +++ b/testdata/algo/l2n_writer_au.txt @@ -162,6 +162,8 @@ circuit(INV2 param(W 0.95) param(AS 0.49875) param(AD 0.26125) + param(PS 2.95) + param(PD 1.5) terminal(S $2) terminal(G IN) terminal(D $5) @@ -172,6 +174,8 @@ circuit(INV2 param(W 0.95) param(AS 0.26125) param(AD 0.49875) + param(PS 1.5) + param(PD 2.95) terminal(S $5) terminal(G $2) terminal(D OUT) @@ -182,6 +186,8 @@ circuit(INV2 param(W 0.95) param(AS 0.49875) param(AD 0.26125) + param(PS 2.95) + param(PD 1.5) terminal(S $2) terminal(G IN) terminal(D $4) @@ -192,6 +198,8 @@ circuit(INV2 param(W 0.95) param(AS 0.26125) param(AD 0.49875) + param(PS 1.5) + param(PD 2.95) terminal(S $4) terminal(G $2) terminal(D OUT) diff --git a/testdata/algo/l2n_writer_au_2.txt b/testdata/algo/l2n_writer_au_2.txt index f4e5c05c7..847a910a8 100644 --- a/testdata/algo/l2n_writer_au_2.txt +++ b/testdata/algo/l2n_writer_au_2.txt @@ -198,6 +198,8 @@ circuit(INV2 param(W 0.95) param(AS 0.49875) param(AD 0.26125) + param(PS 2.95) + param(PD 1.5) terminal(S $3) terminal(G IN) terminal(D VDD) @@ -209,6 +211,8 @@ circuit(INV2 param(W 0.95) param(AS 0.26125) param(AD 0.49875) + param(PS 1.5) + param(PD 2.95) terminal(S VDD) terminal(G $3) terminal(D OUT) @@ -220,6 +224,8 @@ circuit(INV2 param(W 0.95) param(AS 0.49875) param(AD 0.26125) + param(PS 2.95) + param(PD 1.5) terminal(S $3) terminal(G IN) terminal(D VSS) @@ -231,6 +237,8 @@ circuit(INV2 param(W 0.95) param(AS 0.26125) param(AD 0.49875) + param(PS 1.5) + param(PD 2.95) terminal(S VSS) terminal(G $3) terminal(D OUT) diff --git a/testdata/algo/l2n_writer_au_2s.txt b/testdata/algo/l2n_writer_au_2s.txt index 2465db56d..034f62c26 100644 --- a/testdata/algo/l2n_writer_au_2s.txt +++ b/testdata/algo/l2n_writer_au_2s.txt @@ -174,6 +174,8 @@ X(INV2 E(W 0.95) E(AS 0.49875) E(AD 0.26125) + E(PS 2.95) + E(PD 1.5) T(S $3) T(G IN) T(D VDD) @@ -185,6 +187,8 @@ X(INV2 E(W 0.95) E(AS 0.26125) E(AD 0.49875) + E(PS 1.5) + E(PD 2.95) T(S VDD) T(G $3) T(D OUT) @@ -196,6 +200,8 @@ X(INV2 E(W 0.95) E(AS 0.49875) E(AD 0.26125) + E(PS 2.95) + E(PD 1.5) T(S $3) T(G IN) T(D VSS) @@ -207,6 +213,8 @@ X(INV2 E(W 0.95) E(AS 0.26125) E(AD 0.49875) + E(PS 1.5) + E(PD 2.95) T(S VSS) T(G $3) T(D OUT) diff --git a/testdata/algo/l2n_writer_au_s.txt b/testdata/algo/l2n_writer_au_s.txt index 2ff91db45..726395991 100644 --- a/testdata/algo/l2n_writer_au_s.txt +++ b/testdata/algo/l2n_writer_au_s.txt @@ -140,6 +140,8 @@ X(INV2 E(W 0.95) E(AS 0.49875) E(AD 0.26125) + E(PS 2.95) + E(PD 1.5) T(S $2) T(G IN) T(D $5) @@ -150,6 +152,8 @@ X(INV2 E(W 0.95) E(AS 0.26125) E(AD 0.49875) + E(PS 1.5) + E(PD 2.95) T(S $5) T(G $2) T(D OUT) @@ -160,6 +164,8 @@ X(INV2 E(W 0.95) E(AS 0.49875) E(AD 0.26125) + E(PS 2.95) + E(PD 1.5) T(S $2) T(G IN) T(D $4) @@ -170,6 +176,8 @@ X(INV2 E(W 0.95) E(AS 0.26125) E(AD 0.49875) + E(PS 1.5) + E(PD 2.95) T(S $4) T(G $2) T(D OUT) diff --git a/testdata/algo/nwriter5_au.txt b/testdata/algo/nwriter5_au.txt index 2f6212938..3c1b7df3c 100644 --- a/testdata/algo/nwriter5_au.txt +++ b/testdata/algo/nwriter5_au.txt @@ -10,7 +10,7 @@ * net 3 n3 * net 4 n4 * device instance $1 0,0 M3CLS -M$1 1 4 3 1 MM3CLS L=0.25U W=0.18U AS=1.2U AD=0.75U +M$1 1 4 3 1 MM3CLS L=0.25U W=0.18U AS=1.2U AD=0.75U PS=2.2U PD=1.75U * device instance $2 0,0 M3CLS -M$2 3 4 2 3 MM3CLS L=1.4U W=0.25U AS=1.3U AD=0.85U +M$2 3 4 2 3 MM3CLS L=1.4U W=0.25U AS=1.3U AD=0.85U PS=2.3U PD=1.85U .ENDS C1 diff --git a/testdata/algo/nwriter6_au.txt b/testdata/algo/nwriter6_au.txt index caae620b9..349757227 100644 --- a/testdata/algo/nwriter6_au.txt +++ b/testdata/algo/nwriter6_au.txt @@ -12,7 +12,7 @@ * net 4 n4 * net 5 n5 * device instance $1 0,0 M4CLS -M$1 1 4 3 5 1 MM4CLS L=0.25U W=0.18U AS=1.2U AD=0.75U +M$1 1 4 3 5 1 MM4CLS L=0.25U W=0.18U AS=1.2U AD=0.75U PS=2.2U PD=1.75U * device instance $2 0,0 M4CLS -M$2 3 4 2 5 3 MM4CLS L=1.4U W=0.25U AS=1.3U AD=0.85U +M$2 3 4 2 5 3 MM4CLS L=1.4U W=0.25U AS=1.3U AD=0.85U PS=2.3U PD=1.85U .ENDS C1 diff --git a/testdata/algo/nwriter8_au.txt b/testdata/algo/nwriter8_au.txt index b17e53ee2..f5d4cb619 100644 --- a/testdata/algo/nwriter8_au.txt +++ b/testdata/algo/nwriter8_au.txt @@ -28,7 +28,7 @@ XSC2 3 2 4 3 C1 * net 4 n4 * net 5 n5 * device instance $1 0,0 M4CLS -M$1 1 4 3 5 1 MM4CLS L=0.25U W=0.18U AS=1.2U AD=0.75U +M$1 1 4 3 5 1 MM4CLS L=0.25U W=0.18U AS=1.2U AD=0.75U PS=2.2U PD=1.75U * device instance $2 0,0 M4CLS -M$2 3 4 2 5 3 MM4CLS L=1.4U W=0.25U AS=1.3U AD=0.85U +M$2 3 4 2 5 3 MM4CLS L=1.4U W=0.25U AS=1.3U AD=0.85U PS=2.3U PD=1.85U .ENDS C1 diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index 89e920d2b..c6f5ac5df 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -279,10 +279,10 @@ Circuit RINGO (): XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD) XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD) Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5): - DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125] - DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875] - DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125] - DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875] + DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5] + DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95] + DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5] + DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95] XTRANS $1 ($1=$2,$2=$4,$3=IN) XTRANS $2 ($1=$2,$2=$5,$3=IN) XTRANS $3 ($1=$5,$2=OUT,$3=$2) @@ -390,10 +390,10 @@ Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1): XINV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK) XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK) Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK): - DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125] - DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875] - DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125] - DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875] + DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5] + DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95] + DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5] + DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95] XTRANS $1 ($1=$3,$2=VSS,$3=IN) XTRANS $2 ($1=$3,$2=VDD,$3=IN) XTRANS $3 ($1=VDD,$2=OUT,$3=$3) @@ -416,10 +416,10 @@ Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1): XINV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK) XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK) Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK): - DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125] - DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875] - DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125] - DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875] + DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5] + DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95] + DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5] + DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95] END # cleanup now diff --git a/testdata/ruby/dbNetlistDeviceClasses.rb b/testdata/ruby/dbNetlistDeviceClasses.rb index 5f64ff810..7eefab84d 100644 --- a/testdata/ruby/dbNetlistDeviceClasses.rb +++ b/testdata/ruby/dbNetlistDeviceClasses.rb @@ -222,11 +222,15 @@ END d1.set_parameter(RBA::DeviceClassMOS3Transistor::PARAM_W, 2.0) d1.set_parameter(RBA::DeviceClassMOS3Transistor::PARAM_AS, 3.0) d1.set_parameter(RBA::DeviceClassMOS3Transistor::PARAM_AD, 4.0) + d1.set_parameter(RBA::DeviceClassMOS3Transistor::PARAM_PS, 13.0) + d1.set_parameter(RBA::DeviceClassMOS3Transistor::PARAM_PD, 14.0) d2 = circuit.create_device(cls, "d2") d2.set_parameter("L", 1.0) d2.set_parameter("W", 3.0) d2.set_parameter("AS", 4.0) d2.set_parameter("AD", 5.0) + d2.set_parameter("PS", 14.0) + d2.set_parameter("PD", 15.0) pin_a = circuit.create_pin ("A") pin_b = circuit.create_pin ("B") @@ -249,8 +253,8 @@ END assert_equal(nl.to_s, < Date: Sat, 2 Feb 2019 02:33:48 +0100 Subject: [PATCH 232/335] Windows build compatibility. --- src/db/db/dbAsIfFlatRegion.cc | 19 ++-- src/db/db/dbDeepRegion.cc | 102 +++++++++---------- src/db/db/dbEdgePairs.cc | 14 +-- src/db/db/dbEdges.cc | 22 ++-- src/db/db/dbHierNetworkProcessor.cc | 25 ++++- src/db/db/dbHierNetworkProcessor.h | 15 --- src/db/db/dbLayoutToNetlistFormatDefs.cc | 122 ++++++++--------------- src/db/db/dbLayoutToNetlistFormatDefs.h | 46 ++++----- src/db/db/dbRegion.cc | 20 ++-- 9 files changed, 171 insertions(+), 214 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 5e3ac458a..6becce312 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -665,19 +665,16 @@ AsIfFlatRegion::snapped (db::Coord gx, db::Coord gy) return new_region.release (); } -namespace +/** + * @brief A helper class to implement the strange polygon detector + */ +struct DB_PUBLIC StrangePolygonInsideFunc { - /** - * @brief A helper class to implement the strange polygon detector - */ - struct DB_PUBLIC StrangePolygonInsideFunc + inline bool operator() (int wc) const { - inline bool operator() (int wc) const - { - return wc < 0 || wc > 1; - } - }; -} + return wc < 0 || wc > 1; + } +}; RegionDelegate * AsIfFlatRegion::strange_polygon_check () const diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index dd056b17c..df83fbb05 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -34,63 +34,59 @@ namespace db { -namespace +/** + * @brief An iterator delegate for the deep region + * TODO: this is kind of redundant with OriginalLayerIterator .. + */ +class DB_PUBLIC DeepRegionIterator + : public RegionIteratorDelegate { - /** - * @brief An iterator delegate for the deep region - * TODO: this is kind of redundant with OriginalLayerIterator .. - */ - class DB_PUBLIC DeepRegionIterator - : public RegionIteratorDelegate +public: + typedef db::Polygon value_type; + + DeepRegionIterator (const db::RecursiveShapeIterator &iter) + : m_iter (iter) { - public: - typedef db::Polygon value_type; + set (); + } - DeepRegionIterator (const db::RecursiveShapeIterator &iter) - : m_iter (iter) - { - set (); + virtual ~DeepRegionIterator () { } + + virtual bool at_end () const + { + return m_iter.at_end (); + } + + virtual void increment () + { + ++m_iter; + set (); + } + + virtual const value_type *get () const + { + return &m_polygon; + } + + virtual RegionIteratorDelegate *clone () const + { + return new DeepRegionIterator (*this); + } + +private: + friend class Region; + + db::RecursiveShapeIterator m_iter; + mutable value_type m_polygon; + + void set () const + { + if (! m_iter.at_end ()) { + m_iter.shape ().polygon (m_polygon); + m_polygon.transform (m_iter.trans (), false); } - - virtual ~DeepRegionIterator () { } - - virtual bool at_end () const - { - return m_iter.at_end (); - } - - virtual void increment () - { - ++m_iter; - set (); - } - - virtual const value_type *get () const - { - return &m_polygon; - } - - virtual RegionIteratorDelegate *clone () const - { - return new DeepRegionIterator (*this); - } - - private: - friend class Region; - - db::RecursiveShapeIterator m_iter; - mutable value_type m_polygon; - - void set () const - { - if (! m_iter.at_end ()) { - m_iter.shape ().polygon (m_polygon); - m_polygon.transform (m_iter.trans (), false); - } - } - }; - -} + } +}; // ------------------------------------------------------------------------------------------------------------- // DeepRegion implementation diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index cc9ca5d29..dab48738a 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -85,7 +85,7 @@ void EdgePairs::insert (const Sh &shape) flat_edge_pairs ()->insert (shape); } -template void EdgePairs::insert (const db::EdgePair &); +template DB_PUBLIC void EdgePairs::insert (const db::EdgePair &); void EdgePairs::insert (const db::Shape &shape) { @@ -98,9 +98,9 @@ void EdgePairs::insert (const db::Shape &shape, const T &trans) flat_edge_pairs ()->insert (shape, trans); } -template void EdgePairs::insert (const db::Shape &, const db::ICplxTrans &); -template void EdgePairs::insert (const db::Shape &, const db::Trans &); -template void EdgePairs::insert (const db::Shape &, const db::Disp &); +template DB_PUBLIC void EdgePairs::insert (const db::Shape &, const db::ICplxTrans &); +template DB_PUBLIC void EdgePairs::insert (const db::Shape &, const db::Trans &); +template DB_PUBLIC void EdgePairs::insert (const db::Shape &, const db::Disp &); void EdgePairs::clear () { @@ -120,9 +120,9 @@ EdgePairs &EdgePairs::transform (const T &trans) } // explicit instantiations -template EdgePairs &EdgePairs::transform (const db::ICplxTrans &); -template EdgePairs &EdgePairs::transform (const db::Trans &); -template EdgePairs &EdgePairs::transform (const db::Disp &); +template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::ICplxTrans &); +template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::Trans &); +template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::Disp &); const db::RecursiveShapeIterator & EdgePairs::iter () const diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 4ed67922e..f2a797c86 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -133,9 +133,9 @@ Edges &Edges::transform (const T &trans) } // explicit instantiations -template Edges &Edges::transform (const db::ICplxTrans &); -template Edges &Edges::transform (const db::Trans &); -template Edges &Edges::transform (const db::Disp &); +template DB_PUBLIC Edges &Edges::transform (const db::ICplxTrans &); +template DB_PUBLIC Edges &Edges::transform (const db::Trans &); +template DB_PUBLIC Edges &Edges::transform (const db::Disp &); template void Edges::insert (const Sh &shape) @@ -143,11 +143,11 @@ void Edges::insert (const Sh &shape) flat_edges ()->insert (shape); } -template void Edges::insert (const db::Box &); -template void Edges::insert (const db::SimplePolygon &); -template void Edges::insert (const db::Polygon &); -template void Edges::insert (const db::Path &); -template void Edges::insert (const db::Edge &); +template DB_PUBLIC void Edges::insert (const db::Box &); +template DB_PUBLIC void Edges::insert (const db::SimplePolygon &); +template DB_PUBLIC void Edges::insert (const db::Polygon &); +template DB_PUBLIC void Edges::insert (const db::Path &); +template DB_PUBLIC void Edges::insert (const db::Edge &); void Edges::insert (const db::Shape &shape) { @@ -160,9 +160,9 @@ void Edges::insert (const db::Shape &shape, const T &trans) flat_edges ()->insert (shape, trans); } -template void Edges::insert (const db::Shape &, const db::ICplxTrans &); -template void Edges::insert (const db::Shape &, const db::Trans &); -template void Edges::insert (const db::Shape &, const db::Disp &); +template DB_PUBLIC void Edges::insert (const db::Shape &, const db::ICplxTrans &); +template DB_PUBLIC void Edges::insert (const db::Shape &, const db::Trans &); +template DB_PUBLIC void Edges::insert (const db::Shape &, const db::Disp &); FlatEdges * Edges::flat_edges () diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index dd3c40e0f..57272b113 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -302,7 +302,7 @@ local_cluster::ensure_sorted () } template -struct DB_PUBLIC hnp_interaction_receiver +class DB_PUBLIC hnp_interaction_receiver : public box_scanner_receiver2 { public: @@ -829,7 +829,7 @@ local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flag for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { const db::Shapes &shapes = cell.shapes (*l); for (db::Shapes::shape_iterator s = shapes.begin (shape_flags); ! s.at_end (); ++s) { - bs.insert (s->basic_ptr (object_tag), std::make_pair (*l, (attr_id) s->prop_id ())); + bs.insert (s->basic_ptr (object_tag), std::make_pair (*l, (unsigned int) s->prop_id ())); } } @@ -841,6 +841,27 @@ local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flag // explicit instantiations template class DB_PUBLIC local_clusters; +// ------------------------------------------------------------------------------ +// connected_clusters_iterator implementation + +template +connected_clusters_iterator::connected_clusters_iterator (const connected_clusters &c) + : m_lc_iter (c.begin ()) +{ + size_t max_id = 0; + for (typename connected_clusters::const_iterator i = c.begin (); i != c.end (); ++i) { + if (i->id () > max_id) { + max_id = i->id (); + } + } + + m_x_iter = c.m_connections.lower_bound (max_id + 1); + m_x_iter_end = c.m_connections.end (); +} + +// explicit instantiations +template class DB_PUBLIC connected_clusters_iterator; + // ------------------------------------------------------------------------------ // connected_clusters implementation diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index d57db4211..46dc00f54 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -718,21 +718,6 @@ private: std::set m_connected_clusters; }; -template -connected_clusters_iterator::connected_clusters_iterator (const connected_clusters &c) - : m_lc_iter (c.begin ()) -{ - size_t max_id = 0; - for (typename connected_clusters::const_iterator i = c.begin (); i != c.end (); ++i) { - if (i->id () > max_id) { - max_id = i->id (); - } - } - - m_x_iter = c.m_connections.lower_bound (max_id + 1); - m_x_iter_end = c.m_connections.end (); -} - template class cell_clusters_box_converter; /** diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.cc b/src/db/db/dbLayoutToNetlistFormatDefs.cc index 382c408bd..572fdccb8 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.cc +++ b/src/db/db/dbLayoutToNetlistFormatDefs.cc @@ -27,89 +27,47 @@ namespace db namespace l2n_std_format { - static const std::string long_version_key ("version"); - static const std::string long_description_key ("description"); - static const std::string long_top_key ("top"); - static const std::string long_unit_key ("unit"); - static const std::string long_layer_key ("layer"); - static const std::string long_connect_key ("connect"); - static const std::string long_global_key ("global"); - static const std::string long_circuit_key ("circuit"); - static const std::string long_net_key ("net"); - static const std::string long_device_key ("device"); - static const std::string long_polygon_key ("polygon"); - static const std::string long_rect_key ("rect"); - static const std::string long_terminal_key ("terminal"); - static const std::string long_abstract_key ("abstract"); - static const std::string long_param_key ("param"); - static const std::string long_location_key ("location"); - static const std::string long_rotation_key ("rotation"); - static const std::string long_mirror_key ("mirror"); - static const std::string long_scale_key ("scale"); - static const std::string long_pin_key ("pin"); + template<> DB_PUBLIC const std::string keys::version_key ("version"); + template<> DB_PUBLIC const std::string keys::description_key ("description"); + template<> DB_PUBLIC const std::string keys::top_key ("top"); + template<> DB_PUBLIC const std::string keys::unit_key ("unit"); + template<> DB_PUBLIC const std::string keys::layer_key ("layer"); + template<> DB_PUBLIC const std::string keys::connect_key ("connect"); + template<> DB_PUBLIC const std::string keys::global_key ("global"); + template<> DB_PUBLIC const std::string keys::circuit_key ("circuit"); + template<> DB_PUBLIC const std::string keys::net_key ("net"); + template<> DB_PUBLIC const std::string keys::device_key ("device"); + template<> DB_PUBLIC const std::string keys::polygon_key ("polygon"); + template<> DB_PUBLIC const std::string keys::rect_key ("rect"); + template<> DB_PUBLIC const std::string keys::terminal_key ("terminal"); + template<> DB_PUBLIC const std::string keys::abstract_key ("abstract"); + template<> DB_PUBLIC const std::string keys::param_key ("param"); + template<> DB_PUBLIC const std::string keys::location_key ("location"); + template<> DB_PUBLIC const std::string keys::rotation_key ("rotation"); + template<> DB_PUBLIC const std::string keys::mirror_key ("mirror"); + template<> DB_PUBLIC const std::string keys::scale_key ("scale"); + template<> DB_PUBLIC const std::string keys::pin_key ("pin"); - static const std::string short_version_key ("V"); - static const std::string short_description_key ("B"); - static const std::string short_top_key ("W"); - static const std::string short_unit_key ("U"); - static const std::string short_layer_key ("L"); - static const std::string short_connect_key ("C"); - static const std::string short_global_key ("G"); - static const std::string short_circuit_key ("X"); - static const std::string short_net_key ("N"); - static const std::string short_device_key ("D"); - static const std::string short_polygon_key ("Q"); - static const std::string short_rect_key ("R"); - static const std::string short_terminal_key ("T"); - static const std::string short_abstract_key ("A"); - static const std::string short_param_key ("E"); - static const std::string short_location_key ("Y"); - static const std::string short_rotation_key ("O"); - static const std::string short_mirror_key ("M"); - static const std::string short_scale_key ("S"); - static const std::string short_pin_key ("P"); - - template<> const std::string &keys::version_key = long_version_key; - template<> const std::string &keys::description_key = long_description_key; - template<> const std::string &keys::top_key = long_top_key; - template<> const std::string &keys::unit_key = long_unit_key; - template<> const std::string &keys::layer_key = long_layer_key; - template<> const std::string &keys::connect_key = long_connect_key; - template<> const std::string &keys::global_key = long_global_key; - template<> const std::string &keys::circuit_key = long_circuit_key; - template<> const std::string &keys::net_key = long_net_key; - template<> const std::string &keys::device_key = long_device_key; - template<> const std::string &keys::polygon_key = long_polygon_key; - template<> const std::string &keys::rect_key = long_rect_key; - template<> const std::string &keys::terminal_key = long_terminal_key; - template<> const std::string &keys::abstract_key = long_abstract_key; - template<> const std::string &keys::param_key = long_param_key; - template<> const std::string &keys::location_key = long_location_key; - template<> const std::string &keys::rotation_key = long_rotation_key; - template<> const std::string &keys::mirror_key = long_mirror_key; - template<> const std::string &keys::scale_key = long_scale_key; - template<> const std::string &keys::pin_key = long_pin_key; - - template<> const std::string &keys::version_key = short_version_key; - template<> const std::string &keys::description_key = short_description_key; - template<> const std::string &keys::top_key = short_top_key; - template<> const std::string &keys::unit_key = short_unit_key; - template<> const std::string &keys::layer_key = short_layer_key; - template<> const std::string &keys::connect_key = short_connect_key; - template<> const std::string &keys::global_key = short_global_key; - template<> const std::string &keys::circuit_key = short_circuit_key; - template<> const std::string &keys::net_key = short_net_key; - template<> const std::string &keys::device_key = short_device_key; - template<> const std::string &keys::polygon_key = short_polygon_key; - template<> const std::string &keys::rect_key = short_rect_key; - template<> const std::string &keys::terminal_key = short_terminal_key; - template<> const std::string &keys::abstract_key = short_abstract_key; - template<> const std::string &keys::param_key = short_param_key; - template<> const std::string &keys::location_key = short_location_key; - template<> const std::string &keys::rotation_key = short_rotation_key; - template<> const std::string &keys::mirror_key = short_mirror_key; - template<> const std::string &keys::scale_key = short_scale_key; - template<> const std::string &keys::pin_key = short_pin_key; + template<> DB_PUBLIC const std::string keys::version_key ("V"); + template<> DB_PUBLIC const std::string keys::description_key ("B"); + template<> DB_PUBLIC const std::string keys::top_key ("W"); + template<> DB_PUBLIC const std::string keys::unit_key ("U"); + template<> DB_PUBLIC const std::string keys::layer_key ("L"); + template<> DB_PUBLIC const std::string keys::connect_key ("C"); + template<> DB_PUBLIC const std::string keys::global_key ("G"); + template<> DB_PUBLIC const std::string keys::circuit_key ("X"); + template<> DB_PUBLIC const std::string keys::net_key ("N"); + template<> DB_PUBLIC const std::string keys::device_key ("D"); + template<> DB_PUBLIC const std::string keys::polygon_key ("Q"); + template<> DB_PUBLIC const std::string keys::rect_key ("R"); + template<> DB_PUBLIC const std::string keys::terminal_key ("T"); + template<> DB_PUBLIC const std::string keys::abstract_key ("A"); + template<> DB_PUBLIC const std::string keys::param_key ("E"); + template<> DB_PUBLIC const std::string keys::location_key ("Y"); + template<> DB_PUBLIC const std::string keys::rotation_key ("O"); + template<> DB_PUBLIC const std::string keys::mirror_key ("M"); + template<> DB_PUBLIC const std::string keys::scale_key ("S"); + template<> DB_PUBLIC const std::string keys::pin_key ("P"); } } diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index ed57f2d03..d28de3503 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -103,29 +103,29 @@ namespace l2n_std_format template struct DB_PUBLIC keys { - static const std::string &version_key, - &description_key, - &top_key, - &unit_key, - &layer_key, - &connect_key, - &global_key, - &circuit_key, - &net_key, - &device_key, - &subcircuit_key, - &polygon_key, - &rect_key, - &terminal_key, - &abstract_key, - ¶m_key, - &location_key, - &rotation_key, - &mirror_key, - &scale_key, - &pin_key, - &indent1, - &indent2; + static const std::string version_key; + static const std::string description_key; + static const std::string top_key; + static const std::string unit_key; + static const std::string layer_key; + static const std::string connect_key; + static const std::string global_key; + static const std::string circuit_key; + static const std::string net_key; + static const std::string device_key; + static const std::string subcircuit_key; + static const std::string polygon_key; + static const std::string rect_key; + static const std::string terminal_key; + static const std::string abstract_key; + static const std::string param_key; + static const std::string location_key; + static const std::string rotation_key; + static const std::string mirror_key; + static const std::string scale_key; + static const std::string pin_key; + static const std::string indent1; + static const std::string indent2; inline static bool is_short () { return Short; } }; diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 2c0f9bbd5..18562208d 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -122,9 +122,9 @@ Region &Region::transform (const T &trans) } // explicit instantiations -template Region &Region::transform (const db::ICplxTrans &); -template Region &Region::transform (const db::Trans &); -template Region &Region::transform (const db::Disp &); +template DB_PUBLIC Region &Region::transform (const db::ICplxTrans &); +template DB_PUBLIC Region &Region::transform (const db::Trans &); +template DB_PUBLIC Region &Region::transform (const db::Disp &); template void Region::insert (const Sh &shape) @@ -132,10 +132,10 @@ void Region::insert (const Sh &shape) flat_region ()->insert (shape); } -template void Region::insert (const db::Box &); -template void Region::insert (const db::SimplePolygon &); -template void Region::insert (const db::Polygon &); -template void Region::insert (const db::Path &); +template DB_PUBLIC void Region::insert (const db::Box &); +template DB_PUBLIC void Region::insert (const db::SimplePolygon &); +template DB_PUBLIC void Region::insert (const db::Polygon &); +template DB_PUBLIC void Region::insert (const db::Path &); void Region::insert (const db::Shape &shape) { @@ -148,9 +148,9 @@ void Region::insert (const db::Shape &shape, const T &trans) flat_region ()->insert (shape, trans); } -template void Region::insert (const db::Shape &, const db::ICplxTrans &); -template void Region::insert (const db::Shape &, const db::Trans &); -template void Region::insert (const db::Shape &, const db::Disp &); +template DB_PUBLIC void Region::insert (const db::Shape &, const db::ICplxTrans &); +template DB_PUBLIC void Region::insert (const db::Shape &, const db::Trans &); +template DB_PUBLIC void Region::insert (const db::Shape &, const db::Disp &); FlatRegion * Region::flat_region () From 99f111fe01879998a9412f424cb60e773997a194 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 Feb 2019 22:43:42 +0100 Subject: [PATCH 233/335] Attempt to achieve reproducibility between MSVC and gcc Applies to dbHierProcessor.cc: The issue was related to std::unordered_set/map which (as the name says) is not ordered. The output of the boolean core computation step is currently dependent on the order (it's single pass), hence the order of the contexts matters. Using ordered sets where possible and explicit sorting might help. --- src/db/db/dbHash.h | 16 +++++++ src/db/db/dbHierProcessor.cc | 74 ++++++++++++++++++++---------- src/db/db/dbHierProcessor.h | 24 +++++----- testdata/algo/deep_region_au6.gds | Bin 9818 -> 9818 bytes testdata/algo/hlp12.oas | Bin 868 -> 832 bytes testdata/algo/hlp14.oas | Bin 526 -> 513 bytes testdata/algo/hlp6.oas | Bin 2671 -> 2637 bytes 7 files changed, 77 insertions(+), 37 deletions(-) diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index 5f5ecb481..5c14ce12a 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -404,6 +404,22 @@ namespace std return hf; } }; + + /** + * @brief Generic hash for an ordered set + */ + template + struct hash > + { + size_t operator() (const std::set &o) const + { + size_t hf = 0; + for (typename std::set::const_iterator i = o.begin (); i != o.end (); ++i) { + hf = hfunc (hf, std::hash () (*i)); + } + return hf; + } + }; } #endif diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index a2f4d83d6..73fb42941 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -242,14 +242,14 @@ LocalProcessorCellContexts::LocalProcessorCellContexts (const db::Cell *intruder } db::LocalProcessorCellContext * -LocalProcessorCellContexts::find_context (const key_type &intruders) +LocalProcessorCellContexts::find_context (const context_key_type &intruders) { - std::unordered_map::iterator c = m_contexts.find (intruders); + std::unordered_map::iterator c = m_contexts.find (intruders); return c != m_contexts.end () ? &c->second : 0; } db::LocalProcessorCellContext * -LocalProcessorCellContexts::create (const key_type &intruders) +LocalProcessorCellContexts::create (const context_key_type &intruders) { return &m_contexts[intruders]; } @@ -290,6 +290,18 @@ subtract (std::unordered_set &res, const std::unordered_set &a, const std::pair &b) + { + return *a.first < *b.first; + } +}; + +} + void LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, const LocalProcessor *proc) { @@ -300,7 +312,19 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte int index = 0; int total = int (m_contexts.size ()); - for (std::unordered_map::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { + + // NOTE: we use the ordering provided by key_type::operator< rather than the unordered map to achieve + // reproducability across different platforms. unordered_map is faster, but for processing them, + // strict ordering is a more robust choice. + std::vector > sorted_contexts; + sorted_contexts.reserve (m_contexts.size ()); + for (std::unordered_map::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { + sorted_contexts.push_back (std::make_pair (&c->first, &c->second)); + } + + std::sort (sorted_contexts.begin (), sorted_contexts.end (), context_sorter ()); + + for (std::vector >::const_iterator c = sorted_contexts.begin (); c != sorted_contexts.end (); ++c) { ++index; @@ -311,31 +335,31 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte if (first) { { - tl::MutexLocker locker (&c->second.lock ()); - common = c->second.propagated (); + tl::MutexLocker locker (&c->second->lock ()); + common = c->second->propagated (); } CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell) - proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, common); + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, common); first = false; } else { std::unordered_set res; { - tl::MutexLocker locker (&c->second.lock ()); - res = c->second.propagated (); + tl::MutexLocker locker (&c->second->lock ()); + res = c->second->propagated (); } { CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell) - proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, res); + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, res); } if (common.empty ()) { CRONOLOGY_COMPUTE_BRACKET(event_propagate) - c->second.propagate (res); + c->second->propagate (res); } else if (res != common) { @@ -354,8 +378,8 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte if (! lost.empty ()) { subtract (common, lost, cell->layout (), proc->max_vertex_count (), proc->area_ratio ()); - for (std::unordered_map::iterator cc = m_contexts.begin (); cc != c; ++cc) { - cc->second.propagate (lost); + for (std::vector >::const_iterator cc = sorted_contexts.begin (); cc != c; ++cc) { + cc->second->propagate (lost); } } @@ -373,7 +397,7 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte subtract (gained, common, cell->layout (), proc->max_vertex_count (), proc->area_ratio ()); if (! gained.empty ()) { - c->second.propagate (gained); + c->second->propagate (gained); } } @@ -740,7 +764,7 @@ private: // --------------------------------------------------------------------------------------------- // LocalProcessorContextComputationTask implementation -LocalProcessorContextComputationTask::LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::unordered_set > &intruders, db::Coord dist) +LocalProcessorContextComputationTask::LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist) : tl::Task (), mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context), mp_subject_parent (subject_parent), mp_subject_cell (subject_cell), m_subject_cell_inst (subject_cell_inst), @@ -849,7 +873,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const L contexts.set_intruder_layer (intruder_layer); contexts.set_subject_layer (subject_layer); - std::pair, std::unordered_set > intruders; + LocalProcessorCellContexts::context_key_type intruders; issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ()); if (mp_cc_job.get ()) { @@ -869,7 +893,7 @@ void LocalProcessor::issue_compute_contexts (LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, - std::pair, std::unordered_set > &intruders, + LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist) const { bool is_small_job = subject_cell->begin ().at_end (); @@ -887,7 +911,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, - const std::pair, std::unordered_set > &intruders, + const LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist) const { CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts) @@ -1008,7 +1032,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, } - for (std::unordered_set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { if (! inst_bci (*i).empty ()) { scanner.insert2 (i.operator-> (), ++id); } @@ -1028,7 +1052,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, } } - for (std::unordered_set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert2 (i.operator-> (), 0); } @@ -1053,7 +1077,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, if (! nbox.empty ()) { - std::pair, std::unordered_set > intruders_below; + LocalProcessorCellContexts::context_key_type intruders_below; db::shape_reference_translator_with_trans rt (mp_subject_layout, tni); @@ -1173,7 +1197,7 @@ LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOp } void -LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const db::LocalOperation *op, const std::pair, std::unordered_set > &intruders, std::unordered_set &result) const +LocalProcessor::compute_local_cell (const db::LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const LocalProcessorCellContexts::context_key_type &intruders, std::unordered_set &result) const { const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ()); @@ -1221,7 +1245,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: } // TODO: TODO: can we confine this search to the subject's (sized) bounding box? - for (std::unordered_set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert (i.operator-> (), interactions.next_id ()); } @@ -1239,7 +1263,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: } // TODO: can we confine this search to the subject's (sized) bounding box? - for (std::unordered_set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { scanner.insert2 (i.operator-> (), interactions.next_id ()); } @@ -1284,7 +1308,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db:: } // TODO: can we confine this search to the subject's (sized) bounding box? - for (std::unordered_set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { if (! inst_bci (*i).empty ()) { scanner.insert2 (i.operator-> (), ++inst_id); } diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index 5ed1ecb4e..5bc2cf4d1 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -152,15 +152,15 @@ private: class DB_PUBLIC LocalProcessorCellContexts { public: - typedef std::pair, std::unordered_set > key_type; - typedef std::unordered_map map_type; - typedef map_type::const_iterator iterator; + typedef std::pair, std::set > context_key_type; + typedef std::unordered_map context_map_type; + typedef context_map_type::const_iterator iterator; LocalProcessorCellContexts (); LocalProcessorCellContexts (const db::Cell *intruder_cell); - db::LocalProcessorCellContext *find_context (const key_type &intruders); - db::LocalProcessorCellContext *create (const key_type &intruders); + db::LocalProcessorCellContext *find_context (const context_key_type &intruders); + db::LocalProcessorCellContext *create (const context_key_type &intruders); void compute_results (const LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, const LocalProcessor *proc); iterator begin () const @@ -175,7 +175,7 @@ public: private: const db::Cell *mp_intruder_cell; - std::unordered_map m_contexts; + std::unordered_map m_contexts; }; class DB_PUBLIC LocalProcessorContexts @@ -260,7 +260,7 @@ class DB_PUBLIC LocalProcessorContextComputationTask : public tl::Task { public: - LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::unordered_set > &intruders, db::Coord dist); + LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist); void perform (); private: @@ -271,7 +271,7 @@ private: db::Cell *mp_subject_cell; db::ICplxTrans m_subject_cell_inst; const db::Cell *mp_intruder_cell; - std::pair, std::unordered_set > m_intruders; + LocalProcessorCellContexts::context_key_type m_intruders; db::Coord m_dist; }; @@ -382,11 +382,11 @@ private: mutable std::auto_ptr > mp_cc_job; std::string description (const LocalOperation *op) const; - void compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::unordered_set > &intruders, db::Coord dist) const; - void do_compute_contexts (db::LocalProcessorCellContext *cell_context, const db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair, std::unordered_set > &intruders, db::Coord dist) const; - void issue_compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair, std::unordered_set > &intruders, db::Coord dist) const; + void compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist) const; + void do_compute_contexts (db::LocalProcessorCellContext *cell_context, const db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist) const; + void issue_compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist) const; void push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set &result) const; - void compute_local_cell (const db::LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const std::pair, std::unordered_set > &intruders, std::unordered_set &result) const; + void compute_local_cell (const db::LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const LocalProcessorCellContexts::context_key_type &intruders, std::unordered_set &result) const; }; } diff --git a/testdata/algo/deep_region_au6.gds b/testdata/algo/deep_region_au6.gds index 6609f6f4071cc0c2333e0ae7ac461b2abbb344db..9fecea99fb9bb8f92637ea72e40ec2d1129e3f82 100644 GIT binary patch delta 1662 zcmaJ>JxE(o6h3*4iHRnt)fb!ko0#x;SRF*IP(ln@sv$_JB3X0_Z|Lk;T9pXrVY12RpXNqf_S=nx5|+uW_-2%Xja0zVmnQdAY1z)*2Ke%UY+Qh!y`% zmi%lQq?y}$>a=3Drxl|9dmW|4SM$qf?|v;*KThwzX*{Q)jywH6G&qvEBPK_!eg0up z_`oW0F*E!6FvVVGh7Thtb|!#bd@wv4qL@S8%S?{M>3~xpe>naz=Pl*x( zM8NLPMEPVJ4WAg`FCE$9WKXO0w?H)6LlpT&^r&Ib@W4=7K2oZDq>{zo1`U5pss%ub z0w6Vr0>EDvuuST`Od74WWYF+8Q6Ygh5YZqtX!x79REoABL0bkP1%EAFT~R3zlC`}y zLaO%g1~&Fp3WRj?XB#0^dnmQeRSJYOnr$PbYKJtpv9F!N8&80!iiYQ8V#+qxZk}P` z?B-&ub5$ZJJdsS%@S(Jxa&2@2)9Q+v>dnxEQJC9QYHm{{^Y0BBUZpYDHi6CJhcm@TwgKuXnq)nQJrWg`#>T+&-ug&`e;}=ABNxXH=>)O0;mM!IG|xn}G?4 zJkwjDX*cEend#AKA*wfdNrK@PkYoH*d&ype>2ckLG7@~Gkmi#rRXeA7E01QTRHqW}dHgB;Ho{(CnH{YAvU=Iwua(*E32E{%hI`Q$cg0h@ZS;MjyyJyQ@G(k?*kesm>y*DPj_yltHC0=t> z;)$;(evbG#up{?Km0#qaurIz2{DpI%lYJodIT=AL@$ASiYClGjHsxo!3f f+Z=#Ht;kWFd@v@tGbKKln2N>a+lZl(@jOUuoi^gS&l+dltrR$;*bR9*ll07ARf*7-}enre8bvjBprZXlb5T#-%*Z?UH z--rYvW#sb}VU;?G6?=<>nP3%aUC-7#9-Kfa5UG=VGEJn8H^@l!XYjEhq3cu(Yu9-m@hnyZ3 zX?j>JbF-RS7pqYxFWH(I+LBR0j5_dwwv2vr^-yFG^74Lg0=XAh%Duoxf_tHJ~P|y|5|WPv2^4T~gTEaV8Re;y4+8;zF(evmy-}f!G+Snp(%$ z2`7VVg%XHT&D=XozYmd@nIQkp1i4I`Y2W4dY?YjBg}heQ@v!bg=e5VQ+3TSvyFotI zIo5I;TZI}{mF$iRt+nAlkiB`X6)RNgVf{X@@wD;N&i+p31J2=h&*FFXd-wS1 zU>$_VWU|M_dlwU#OpkM1?(x~{<#|8O_t@)q)~MV&P9FPguDhuBGLgaixt!@0+1JCw z{>b9B&%JJ8gM4_A&F?nX>;kWO{zjhX=Nar$nX>ImR*`K6u&Zy>M$}jNM2$*$ivO> zK~xANb%Aw-$PK;~lA<;ssYOgTln(MRh?+36t&q&Wz?%0$^%CPU79`VvG7nr9F}+mH zyU3dVgO!`@f`|dw9s!UE3Kv)xiJg>MA&H~|DEvYd delta 207 zcmX@W_JnQ2BF6d)tQS~!Fu&m8X1yTdAOHfQHjGRi>R*^=b9`my<_%cD_rSBRXHS|Q2Jc0okI0B%BqSitr{R8*u* z5y<@DYzty9TgeFIY~Ta2k6mJ95EXg$8$`A(Wdf4R&Vk5d-c6 delta 40 ycmV+@0N4M41dard;vLQyhI@+R04M`6_AjPA-xw!% zvls>#Ffy%ho4~weClko<4u=`eE!k&gFmp3(P!bTe0EtX6ZP~{7WHOT|P-%z53-*?5 zpg7A5%Nfp{d>c7|8lS9WhN#@(xkX?StB5(+jX(*Y9Uuvi8+$%89qDBfwS~HIg3OJH zEZhuxSOq|)UeLH9``{qs%uM(F=ZGmD>klUWhzn8gMWd4z@`H)&co1)E7C)T8@`bp z6b1(Ap@Lv)t8}nK#ECsrh~gm!g$O<5Agr=6BFOOj(pE1IKJWYE^W(iw<9p*|U+fnM zyS0nI%Nu)(?V`SKGSgrXPxkxJBN7zfnQpK>>(Un5!-T+#sWihZR1uiVpo%?y0?g3E01_rh Date: Sun, 3 Feb 2019 01:48:14 +0100 Subject: [PATCH 234/335] More details control over verbosity of region ops Plus: the region op control attributes (threads, min_coherence etc.) got lost when replacing the delegate of a region. Now they are maintained in most cases. --- src/db/db/dbAsIfFlatRegion.cc | 21 ++++++++++++++ src/db/db/dbDeepRegion.cc | 6 ++++ src/db/db/dbEdges.cc | 8 ++++-- src/db/db/dbEdges.h | 2 +- src/db/db/dbFlatRegion.cc | 6 ++++ src/db/db/dbHierProcessor.cc | 29 ++++++++++++-------- src/db/db/dbHierProcessor.h | 11 ++++++++ src/db/db/dbLayout.cc | 14 ++++++---- src/db/db/dbNetlistDeviceExtractor.cc | 1 + src/db/db/dbNetlistDeviceExtractorClasses.cc | 1 + src/db/db/dbOriginalLayerRegion.cc | 3 ++ src/db/db/dbRegion.h | 20 ++++++++++++++ src/db/db/dbRegionDelegate.cc | 7 +++++ src/db/db/dbRegionDelegate.h | 7 +++++ src/db/db/gsiDeclDbRegion.cc | 14 ++++++++++ 15 files changed, 129 insertions(+), 21 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 6becce312..3e97ea64d 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -458,6 +458,9 @@ RegionDelegate * AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const { db::EdgeProcessor ep (report_progress (), progress_desc ()); + if (base_verbosity ()) { + ep.set_base_verbosity (base_verbosity ()); + } // shortcut if (empty ()) { @@ -1055,6 +1058,9 @@ AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc) const } else { db::EdgeProcessor ep (report_progress (), progress_desc ()); + if (base_verbosity ()) { + ep.set_base_verbosity (base_verbosity ()); + } // count edges and reserve memory size_t n = 0; @@ -1132,6 +1138,9 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const // Generic case - the size operation will merge first db::EdgeProcessor ep (report_progress (), progress_desc ()); + if (base_verbosity ()) { + ep.set_base_verbosity (base_verbosity ()); + } // count edges and reserve memory size_t n = 0; @@ -1213,6 +1222,9 @@ AsIfFlatRegion::and_with (const Region &other) const // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); + if (base_verbosity ()) { + ep.set_base_verbosity (base_verbosity ()); + } // count edges and reserve memory size_t n = 0; @@ -1267,6 +1279,9 @@ AsIfFlatRegion::not_with (const Region &other) const // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); + if (base_verbosity ()) { + ep.set_base_verbosity (base_verbosity ()); + } // count edges and reserve memory size_t n = 0; @@ -1319,6 +1334,9 @@ AsIfFlatRegion::xor_with (const Region &other) const // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); + if (base_verbosity ()) { + ep.set_base_verbosity (base_verbosity ()); + } // count edges and reserve memory size_t n = 0; @@ -1372,6 +1390,9 @@ AsIfFlatRegion::or_with (const Region &other) const // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); + if (base_verbosity ()) { + ep.set_base_verbosity (base_verbosity ()); + } // count edges and reserve memory size_t n = 0; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index df83fbb05..8488a06c8 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -261,6 +261,9 @@ DeepRegion::ensure_merged_polygons_valid () const m_merged_polygons.clear (); db::EdgeProcessor ep (report_progress (), progress_desc ()); + if (base_verbosity ()) { + ep.set_base_verbosity (base_verbosity ()); + } // count edges and reserve memory size_t n = 0; @@ -347,6 +350,9 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const db::BoolAndOrNotLocalOperation op (and_op); db::LocalProcessor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ()); + if (base_verbosity ()) { + proc.set_base_verbosity (base_verbosity ()); + } proc.set_threads (m_deep_layer.store ()->threads ()); proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ()); proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index f2a797c86..05a8c9ea0 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -59,7 +59,7 @@ Edges::~Edges () Edges &Edges::operator= (const Edges &other) { if (this != &other) { - set_delegate (other.mp_delegate->clone ()); + set_delegate (other.mp_delegate->clone (), false); } return *this; } @@ -100,9 +100,13 @@ Edges::iter () const } void -Edges::set_delegate (EdgesDelegate *delegate) +Edges::set_delegate (EdgesDelegate *delegate, bool keep_attributes) { if (delegate != mp_delegate) { + if (keep_attributes && mp_delegate && delegate) { + // copy attributes (threads, min_coherence etc.) from old to new + *delegate = *mp_delegate; + } delete mp_delegate; mp_delegate = delegate; } diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index efdb7c4b8..c4dfc493f 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -1268,7 +1268,7 @@ private: EdgesDelegate *mp_delegate; - void set_delegate (EdgesDelegate *delegate); + void set_delegate (EdgesDelegate *delegate, bool keep_attributes = true); FlatEdges *flat_edges (); }; diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index 52d6ba07f..d3eb99105 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -107,6 +107,9 @@ FlatRegion::ensure_merged_polygons_valid () const m_merged_polygons.clear (); db::EdgeProcessor ep (report_progress (), progress_desc ()); + if (base_verbosity ()) { + ep.set_base_verbosity (base_verbosity ()); + } // count edges and reserve memory size_t n = 0; @@ -243,6 +246,9 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi invalidate_cache (); db::EdgeProcessor ep (report_progress (), progress_desc ()); + if (base_verbosity ()) { + ep.set_base_verbosity (base_verbosity ()); + } // count edges and reserve memory size_t n = 0; diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 73fb42941..03a2fb5d6 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -255,14 +255,17 @@ LocalProcessorCellContexts::create (const context_key_type &intruders) } static void -subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout, size_t max_vertex_count, double area_ratio) +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout, const db::LocalProcessor *proc) { if (other.empty ()) { return; } + size_t max_vertex_count = proc->max_vertex_count (); + double area_ratio = proc->area_ratio (); + db::EdgeProcessor ep; - ep.set_base_verbosity (60); + ep.set_base_verbosity (proc->base_verbosity () + 30); size_t p1 = 0, p2 = 1; @@ -328,7 +331,7 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte ++index; - if (tl::verbosity () >= 50) { + if (tl::verbosity () >= proc->base_verbosity () + 20) { tl::log << tr ("Computing local results for ") << cell->layout ()->cell_name (cell->cell_index ()) << " (context " << index << "/" << total << ")"; } @@ -374,10 +377,10 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte if (! lost.empty ()) { - subtract (lost, res, cell->layout (), proc->max_vertex_count (), proc->area_ratio ()); + subtract (lost, res, cell->layout (), proc); if (! lost.empty ()) { - subtract (common, lost, cell->layout (), proc->max_vertex_count (), proc->area_ratio ()); + subtract (common, lost, cell->layout (), proc); for (std::vector >::const_iterator cc = sorted_contexts.begin (); cc != c; ++cc) { cc->second->propagate (lost); } @@ -394,7 +397,7 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte if (! gained.empty ()) { - subtract (gained, common, cell->layout (), proc->max_vertex_count (), proc->area_ratio ()); + subtract (gained, common, cell->layout (), proc); if (! gained.empty ()) { c->second->propagate (gained); @@ -822,13 +825,13 @@ LocalProcessorResultComputationTask::perform () // LocalProcessor implementation LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top) - : mp_subject_layout (layout), mp_intruder_layout (layout), mp_subject_top (top), mp_intruder_top (top), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0) + : mp_subject_layout (layout), mp_intruder_layout (layout), mp_subject_top (top), mp_intruder_top (top), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_base_verbosity (30) { // .. nothing yet .. } LocalProcessor::LocalProcessor (db::Layout *subject_layout, db::Cell *subject_top, const db::Layout *intruder_layout, const db::Cell *intruder_top) - : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), mp_subject_top (subject_top), mp_intruder_top (intruder_top), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0) + : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), mp_subject_top (subject_top), mp_intruder_top (intruder_top), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_base_verbosity (30) { // .. nothing yet .. } @@ -844,6 +847,8 @@ std::string LocalProcessor::description (const LocalOperation *op) const void LocalProcessor::run (LocalOperation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer) { + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity, tl::to_string (tr ("Executing ")) + description (op)); + LocalProcessorContexts contexts; compute_contexts (contexts, op, subject_layer, intruder_layer); compute_results (contexts, op, output_layer); @@ -861,7 +866,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const L { try { - tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Computing contexts for ")) + description (op)); + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::to_string (tr ("Computing contexts for ")) + description (op)); if (m_nthreads > 0) { mp_cc_job.reset (new tl::Job (m_nthreads)); @@ -916,7 +921,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, { CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts) - if (tl::verbosity () >= 50) { + if (tl::verbosity () >= m_base_verbosity + 20) { if (! subject_parent) { tl::log << tr ("Computing context for top cell ") << mp_subject_layout->cell_name (subject_cell->cell_index ()); } else { @@ -1114,7 +1119,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, void LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOperation *op, unsigned int output_layer) const { - tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Computing results for ")) + description (op)); + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::to_string (tr ("Computing results for ")) + description (op)); // avoids updates while we work on the layout mp_subject_layout->update (); @@ -1138,7 +1143,7 @@ LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOp while (true) { ++iter; - tl::SelfTimer timer (tl::verbosity () >= 41, tl::sprintf (tl::to_string (tr ("Computing results iteration #%d")), iter)); + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::sprintf (tl::to_string (tr ("Computing results iteration #%d")), iter)); bool any = false; std::unordered_set later; diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index 5bc2cf4d1..720897bd2 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -337,6 +337,16 @@ public: m_description = d; } + void set_base_verbosity (int vb) + { + m_base_verbosity = vb; + } + + int base_verbosity () const + { + return m_base_verbosity; + } + void set_threads (unsigned int nthreads) { m_nthreads = nthreads; @@ -379,6 +389,7 @@ private: unsigned int m_nthreads; size_t m_max_vertex_count; double m_area_ratio; + int m_base_verbosity; mutable std::auto_ptr > mp_cc_job; std::string description (const LocalOperation *op) const; diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 1bbfd432b..af6fea042 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -42,6 +42,8 @@ namespace db { +static const int layout_base_verbosity = 30; + // ----------------------------------------------------------------- // The undo/redo operations @@ -1278,7 +1280,7 @@ Layout::update () const void Layout::do_update () { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Sorting"))); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity, tl::to_string (tr ("Sorting"))); // establish a progress report since this operation can take some time. // HINT: because of some gcc bug, automatic destruction of the tl::Progress @@ -1293,12 +1295,12 @@ Layout::do_update () // the hierarchy management informations if (hier_dirty ()) { { - tl::SelfTimer timer (tl::verbosity () >= 31, "Updating relations"); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Updating relations"); pr->set_desc (tl::to_string (tr ("Updating relations"))); update_relations (); } { - tl::SelfTimer timer (tl::verbosity () >= 31, "Topological sort"); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Topological sort"); pr->set_desc (tl::to_string (tr ("Topological sorting"))); tl_assert (topological_sort ()); } @@ -1316,7 +1318,7 @@ Layout::do_update () if (bboxes_dirty ()) { { - tl::SelfTimer timer (tl::verbosity () >= 31, "Updating bounding boxes"); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Updating bounding boxes"); unsigned int layers = 0; pr->set (0); pr->set_desc (tl::to_string (tr ("Updating bounding boxes"))); @@ -1338,7 +1340,7 @@ Layout::do_update () } { - tl::SelfTimer timer (tl::verbosity () >= 31, "Sorting shapes"); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Sorting shapes"); pr->set (0); pr->set_desc (tl::to_string (tr ("Sorting shapes"))); for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) { @@ -1351,7 +1353,7 @@ Layout::do_update () // sort the instance trees now, since we have computed the bboxes if (hier_dirty () || ! dirty_parents.empty ()) { - tl::SelfTimer timer (tl::verbosity () >= 31, "Sorting instances"); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Sorting instances"); size_t layers = 0; pr->set (0); pr->set_desc (tl::to_string (tr ("Sorting instances"))); diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index d30906149..7f6394ec0 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -271,6 +271,7 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: for (db::recursive_cluster_shape_iterator si (device_clusters, *l, *ci, *c); ! si.at_end(); ++si) { insert_into_region (*si, si.trans (), r); } + r.set_base_verbosity (50); } db::Box box; diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index 8fa72d3ac..a93c13ed6 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -86,6 +86,7 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vectorset_base_verbosity (vb); + } + + /** + * @brief Gets the base verbosity + */ + unsigned int base_verbosity () const + { + return mp_delegate->base_verbosity (); + } + /** * @brief Enable progress reporting * diff --git a/src/db/db/dbRegionDelegate.cc b/src/db/db/dbRegionDelegate.cc index fd1392ec6..fc8381ca9 100644 --- a/src/db/db/dbRegionDelegate.cc +++ b/src/db/db/dbRegionDelegate.cc @@ -30,6 +30,7 @@ namespace db RegionDelegate::RegionDelegate () { + m_base_verbosity = 0; m_report_progress = false; m_merged_semantics = true; m_strict_handling = false; @@ -45,6 +46,7 @@ RegionDelegate & RegionDelegate::operator= (const RegionDelegate &other) { if (this != &other) { + m_base_verbosity = other.m_base_verbosity; m_report_progress = other.m_report_progress; m_merged_semantics = other.m_merged_semantics; m_strict_handling = other.m_strict_handling; @@ -69,6 +71,11 @@ void RegionDelegate::disable_progress () m_report_progress = false; } +void RegionDelegate::set_base_verbosity (int vb) +{ + m_base_verbosity = vb; +} + void RegionDelegate::set_min_coherence (bool f) { m_merge_min_coherence = f; diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index d0a971946..fe1ffcb25 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -80,6 +80,12 @@ public: virtual RegionDelegate *clone () const = 0; + void set_base_verbosity (int vb); + int base_verbosity () const + { + return m_base_verbosity; + } + void enable_progress (const std::string &progress_desc); void disable_progress (); @@ -200,6 +206,7 @@ private: bool m_merge_min_coherence; bool m_report_progress; std::string m_progress_desc; + int m_base_verbosity; }; } diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index f5b1e6877..5b7c47b31 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -2384,6 +2384,20 @@ Class decl_Region ("db", "Region", "@brief Disable progress reporting\n" "Calling this method will disable progress reporting. See \\enable_progress.\n" ) + + method ("base_verbosity=", &db::Region::set_base_verbosity, gsi::arg ("verbosity"), + "@brief Sets the minimum verbosity for timing reports\n" + "Timing reports will be given only if the verbosity is larger than this value. " + "Detailed reports will be given when the verbosity is more than this value plus 10.\n" + "In binary operations, the base verbosity of the first argument is considered.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + method ("base_verbosity", &db::Region::base_verbosity, + "@brief Gets the minimum verbosity for timing reports\n" + "See \\base_verbosity= for details.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + method ("Euclidian", &euclidian_metrics, "@brief Specifies Euclidian metrics for the check functions\n" "This value can be used for the metrics parameter in the check functions, i.e. \\width_check. " From f9c33733b9fdeb459053c9a97da4ce8077837dd3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 3 Feb 2019 01:49:48 +0100 Subject: [PATCH 235/335] l2n format writer and reader: more compact output --- src/db/db/dbLayoutToNetlistFormatDefs.h | 16 +- src/db/db/dbLayoutToNetlistReader.cc | 51 +++--- src/db/db/dbLayoutToNetlistReader.h | 9 +- src/db/db/dbLayoutToNetlistWriter.cc | 56 +++++-- testdata/algo/l2n_writer_au.txt | 162 +++++++++---------- testdata/algo/l2n_writer_au_2.txt | 204 ++++++++++++------------ testdata/algo/l2n_writer_au_2s.txt | 204 ++++++++++++------------ testdata/algo/l2n_writer_au_s.txt | 162 +++++++++---------- 8 files changed, 451 insertions(+), 413 deletions(-) diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index d28de3503..663ca16c5 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -54,17 +54,20 @@ namespace db * top() - specifies the name of the top circuit [short key: W] * layer() - define a layer [short key: L] * connect( ...) - connects layer1 with the following layers [short key: C] - * global( ...) - connects a layer with the given global nets [short key: G] + * global( ...) + * - connects the shapes of the layer with the given global + * nets [short key: G] * circuit( [circuit-def]) - circuit (cell) [short key: X] * device( [device-abstract-def]) * - device abstract [short key: D] * * [circuit-def]: * - * net( [geometry-def]) - net geometry [short key: N] + * net( [geometry-def]) + * - net geometry [short key: N] * A net declaration shall be there also if no geometry - * is present - * pin( ) - outgoing pin connection [short key: P] + * is present. The ID is a numerical shortcut for the net. + * pin( ) - outgoing pin connection [short key: P] * device( [device-def]) * - device with connections [short key: D] * circuit( [circuit-def]) - subcircuit with connections [short key: X] @@ -72,6 +75,7 @@ namespace db * [geometry-def]: * * polygon( ...) - defines a polygon [short key: Q] + * "*" for or means take previous * rect( ) * - defines a rectangle [short key: R] * @@ -85,7 +89,7 @@ namespace db * location( ) - location of the device [short key Y] * must be before terminal * param( ) - defines a parameter [short key E] - * terminal( ) + * terminal( ) * - specifies connection of the terminal with * a net (short key: T) * @@ -95,7 +99,7 @@ namespace db * rotation() - rotation angle (in degree, default is 0) [short key O] * mirror - if specified, the instance is mirrored before rotation [short key M] * scale() - magnification (default is 1) [short key S] - * pin( ) - specifies connection of the pin with a net [short key: P] + * pin( ) - specifies connection of the pin with a net [short key: P] */ namespace l2n_std_format diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index dd9af4889..687c33a12 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -248,20 +248,21 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) circuit->set_cell_index (ci); std::map > connections; + std::map id2net; while (br) { if (test (skeys::net_key) || test (lkeys::net_key)) { - read_net (l2n, circuit); + read_net (l2n, circuit, id2net); } else if (test (skeys::pin_key) || test (lkeys::pin_key)) { - read_pin (l2n, circuit); + read_pin (l2n, circuit, id2net); } else if (test (skeys::device_key) || test (lkeys::device_key)) { std::list conn; - db::CellInstArray ia = read_device (l2n, circuit, conn); + db::CellInstArray ia = read_device (l2n, circuit, conn, id2net); connections[ia] = conn; } else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) { std::list conn; - db::CellInstArray ia = read_subcircuit (l2n, circuit, conn); + db::CellInstArray ia = read_subcircuit (l2n, circuit, conn, id2net); connections[ia] = conn; } else { throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (net, pin, device or circuit expected)"))); @@ -366,9 +367,14 @@ LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n) std::vector pt; + db::Coord x = 0, y = 0; while (br) { - db::Coord x = read_coord (); - db::Coord y = read_coord (); + if (! test ("*")) { + x = read_coord (); + } + if (! test ("*")) { + y = read_coord (); + } pt.push_back (db::Point (x, y)); } @@ -384,17 +390,21 @@ LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n) } void -LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit) +LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map &id2net) { Brace br (this); std::string name; read_word_or_quoted (name); + unsigned int id = (unsigned int) read_int (); + db::Net *net = new db::Net (); net->set_name (name); circuit->add_net (net); + id2net.insert (std::make_pair (id, net)); + db::connected_clusters &cc = l2n->net_clusters ().clusters_per_cell (circuit->cell_index ()); db::local_cluster &lc = *cc.insert (); net->set_cluster_id (lc.id ()); @@ -411,27 +421,26 @@ LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit * } void -LayoutToNetlistStandardReader::read_pin (db::LayoutToNetlist * /*l2n*/, db::Circuit *circuit) +LayoutToNetlistStandardReader::read_pin (db::LayoutToNetlist * /*l2n*/, db::Circuit *circuit, std::map &id2net) { Brace br (this); std::string name; read_word_or_quoted (name); - std::string netname; - read_word_or_quoted (netname); + unsigned int netid = (unsigned int) read_int (); br.done (); const db::Pin &pin = circuit->add_pin (name); - db::Net *net = circuit->net_by_name (netname); + db::Net *net = id2net [netid]; if (!net) { - throw tl::Exception (tl::to_string (tr ("Not a valid net name: ")) + netname); + throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid)); } circuit->connect_pin (pin.id (), net); } db::CellInstArray -LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs) +LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs, std::map &id2net) { Brace br (this); @@ -474,8 +483,7 @@ LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circui Brace br2 (this); std::string tname; read_word_or_quoted (tname); - std::string netname; - read_word_or_quoted (netname); + unsigned int netid = (unsigned int) read_int (); br2.done (); size_t tid = std::numeric_limits::max (); @@ -491,9 +499,9 @@ LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circui throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + tname + tl::to_string (tr (" for device class: ")) + dm->device_class ()->name ()); } - db::Net *net = circuit->net_by_name (netname); + db::Net *net = id2net [netid]; if (!net) { - throw tl::Exception (tl::to_string (tr ("Not a valid net name: ")) + netname); + throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid)); } device->connect_terminal (tid, net); @@ -545,7 +553,7 @@ LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circui } db::CellInstArray -LayoutToNetlistStandardReader::read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs) +LayoutToNetlistStandardReader::read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs, std::map &id2net) { Brace br (this); @@ -617,8 +625,7 @@ LayoutToNetlistStandardReader::read_subcircuit (db::LayoutToNetlist *l2n, db::Ci Brace br2 (this); std::string pname; read_word_or_quoted (pname); - std::string netname; - read_word_or_quoted (netname); + unsigned int netid = (unsigned int) read_int (); br2.done (); const db::Pin *sc_pin = circuit_ref->pin_by_name (pname); @@ -626,9 +633,9 @@ LayoutToNetlistStandardReader::read_subcircuit (db::LayoutToNetlist *l2n, db::Ci throw tl::Exception (tl::to_string (tr ("Not a valid pin name: ")) + pname + tl::to_string (tr (" for circuit: ")) + circuit_ref->name ()); } - db::Net *net = circuit->net_by_name (netname); + db::Net *net = id2net [netid]; if (!net) { - throw tl::Exception (tl::to_string (tr ("Not a valid net name: ")) + netname); + throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid)); } subcircuit->connect_pin (sc_pin->id (), net); diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h index f54fc76ee..04d2f8e74 100644 --- a/src/db/db/dbLayoutToNetlistReader.h +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -40,6 +40,7 @@ class Circuit; class Cell; class DeviceAbstract; class DeviceClass; +class Net; class Region; /** @@ -95,10 +96,10 @@ private: bool at_end (); void skip (); - void read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit); - void read_pin (db::LayoutToNetlist *l2n, db::Circuit *circuit); - db::CellInstArray read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs); - db::CellInstArray read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs); + void read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map &id2net); + void read_pin (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map &id2net); + db::CellInstArray read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs, std::map &id2net); + db::CellInstArray read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs, std::map &id2net); void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc); std::pair read_geometry (db::LayoutToNetlist *l2n); }; diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 1a2092bb2..2be9f008b 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -45,9 +45,9 @@ private: tl::OutputStream *mp_stream; void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit); - void write (const db::LayoutToNetlist *l2n, const db::Net &net); - void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit); - void write (const db::LayoutToNetlist *l2n, const db::Device &device); + void write (const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id); + void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map &net2id); + void write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map &net2id); void write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract); void write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname); }; @@ -171,12 +171,16 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) template void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit) { + std::map net2id; + unsigned int id = 0; + if (circuit.begin_nets () != circuit.end_nets ()) { if (! Keys::is_short ()) { *mp_stream << endl << indent1 << "# Nets with their geometries" << endl; } for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { - write (l2n, *n); + net2id.insert (std::make_pair (n.operator-> (), ++id)); + write (l2n, *n, id); } } @@ -187,7 +191,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Cir for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { const db::Net *net = circuit.net_for_pin (p->id ()); if (net) { - *mp_stream << indent1 << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl; + *mp_stream << indent1 << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << net2id [net] << ")" << endl; } } } @@ -197,7 +201,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Cir *mp_stream << endl << indent1 << "# Devices and their connections" << endl; } for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { - write (l2n, *d); + write (l2n, *d, net2id); } } @@ -206,7 +210,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Cir *mp_stream << endl << indent1 << "# Subcircuits and their connections" << endl; } for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) { - write (l2n, *x); + write (l2n, *x, net2id); } } @@ -218,9 +222,31 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Cir template void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr) { + db::Coord x = 0, y = 0; + bool first = true; for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) { + typename T::point_type pt = tr * *c; - stream << " " << pt.x () << " " << pt.y (); + + stream << " "; + + if (first || pt.x () != x) { + stream << pt.x (); + } else { + stream << "*"; + } + + stream << " "; + + if (first || pt.y () != y) { + stream << pt.y (); + } else { + stream << "*"; + } + + first = false; + x = pt.x (); y = pt.y (); + } } @@ -253,7 +279,7 @@ void std_writer_impl::write (const db::PolygonRef *s, const db::ICplxTrans } template -void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net &net) +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id) { if (! l2n->netlist ()) { throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before extraction has been done"))); @@ -283,7 +309,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net } else { if (! any) { - *mp_stream << indent1 << Keys::net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; + *mp_stream << indent1 << Keys::net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << " " << id << endl; any = true; } @@ -304,12 +330,12 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net if (any) { *mp_stream << indent1 << ")" << endl; } else { - *mp_stream << indent1 << Keys::net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << ")" << endl; + *mp_stream << indent1 << Keys::net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << " " << id << ")" << endl; } } template -void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit) +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map &net2id) { const db::Layout *ly = l2n->internal_layout (); double dbu = ly->dbu (); @@ -344,7 +370,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Sub } else { *mp_stream << " "; } - *mp_stream << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")"; + *mp_stream << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << net2id [net] << ")"; if (separate_lines) { *mp_stream << endl; } @@ -389,7 +415,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Dev } template -void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Device &device) +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map &net2id) { const db::Layout *ly = l2n->internal_layout (); double dbu = ly->dbu (); @@ -410,7 +436,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Dev for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { const db::Net *net = device.net_for_terminal (i->id ()); if (net) { - *mp_stream << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl; + *mp_stream << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << net2id [net] << ")" << endl; } } diff --git a/testdata/algo/l2n_writer_au.txt b/testdata/algo/l2n_writer_au.txt index 03320e97f..9254f0d88 100644 --- a/testdata/algo/l2n_writer_au.txt +++ b/testdata/algo/l2n_writer_au.txt @@ -86,7 +86,7 @@ device(D$NMOS$1 NMOS circuit(INV2 # Nets with their geometries - net(IN + net(IN 1 rect(poly -525 -250 -275 2250) rect(poly -1700 1620 -400 1980) rect(poly -525 -800 -275 800) @@ -94,7 +94,7 @@ circuit(INV2 rect(poly_lbl -801 1799 -799 1801) rect(poly_cont -1630 1690 -1410 1910) ) - net($2 + net($2 2 rect(poly 275 -250 525 2250) rect(poly 220 820 580 1180) rect(poly 275 2000 525 3600) @@ -111,19 +111,19 @@ circuit(INV2 rect(psd -1050 2325 -525 3275) rect(nsd -1050 -475 -525 475) ) - net(OUT + net(OUT 3 rect(diff_cont 690 2890 910 3110) rect(diff_cont 690 2490 910 2710) rect(diff_cont 690 90 910 310) rect(diff_cont 690 -310 910 -90) - polygon(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20) + polygon(metal1 800 20 * 380 940 * * 1620 620 * * 2420 980 * * 1980 1300 * * 20) rect(metal1 620 2420 980 3180) rect(metal1 620 -380 980 380) rect(metal1_lbl 799 1799 801 1801) rect(psd 525 2325 1050 3275) rect(nsd 525 -475 1050 475) ) - net($4 + net($4 4 rect(diff_cont -110 -310 110 -90) rect(diff_cont -110 90 110 310) rect(diff_cont -110 90 110 310) @@ -135,7 +135,7 @@ circuit(INV2 rect(metal2 -1400 -450 1400 450) rect(nsd -275 -475 275 475) ) - net($5 + net($5 5 rect(diff_cont -110 2490 110 2710) rect(diff_cont -110 2890 110 3110) rect(diff_cont -110 2890 110 3110) @@ -149,11 +149,11 @@ circuit(INV2 ) # Outgoing pins and their connections to nets - pin(IN IN) - pin($1 $2) - pin(OUT OUT) - pin($3 $4) - pin($4 $5) + pin(IN 1) + pin($1 2) + pin(OUT 3) + pin($3 4) + pin($4 5) # Devices and their connections device($1 D$PMOS @@ -164,9 +164,9 @@ circuit(INV2 param(AD 0.26125) param(PS 2.95) param(PD 1.5) - terminal(S $2) - terminal(G IN) - terminal(D $5) + terminal(S 2) + terminal(G 1) + terminal(D 5) ) device($2 D$PMOS$1 location(400 2800) @@ -176,9 +176,9 @@ circuit(INV2 param(AD 0.49875) param(PS 1.5) param(PD 2.95) - terminal(S $5) - terminal(G $2) - terminal(D OUT) + terminal(S 5) + terminal(G 2) + terminal(D 3) ) device($3 D$NMOS location(-400 0) @@ -188,9 +188,9 @@ circuit(INV2 param(AD 0.26125) param(PS 2.95) param(PD 1.5) - terminal(S $2) - terminal(G IN) - terminal(D $4) + terminal(S 2) + terminal(G 1) + terminal(D 4) ) device($4 D$NMOS$1 location(400 0) @@ -200,16 +200,16 @@ circuit(INV2 param(AD 0.49875) param(PS 1.5) param(PD 2.95) - terminal(S $4) - terminal(G $2) - terminal(D OUT) + terminal(S 4) + terminal(G 2) + terminal(D 3) ) ) circuit(RINGO # Nets with their geometries - net(FB + net(FB 1 rect(diff_cont 22850 2490 23070 2710) rect(diff_cont 22850 2890 23070 3110) rect(diff_cont 22850 -310 23070 -90) @@ -220,7 +220,7 @@ circuit(RINGO rect(metal2 -1720 1600 23160 2000) rect(metal2_lbl -1 1799 1 1801) ) - net(OSC + net(OSC 2 rect(diff_cont 24450 2890 24670 3110) rect(diff_cont 24450 2490 24670 2710) rect(diff_cont 24450 90 24670 310) @@ -229,7 +229,7 @@ circuit(RINGO rect(metal2 24360 1600 24760 2000) rect(metal2_lbl 24559 1799 24561 1801) ) - net(VSS + net(VSS 3 rect(diff_cont 2530 -310 2750 -90) rect(diff_cont 2530 90 2750 310) rect(diff_cont 2530 90 2750 310) @@ -292,7 +292,7 @@ circuit(RINGO rect(metal1 23580 -380 23940 380) rect(metal2_lbl -1 -1 1 1) ) - net(VDD + net(VDD 4 rect(diff_cont 2530 2490 2750 2710) rect(diff_cont 2530 2890 2750 3110) rect(diff_cont 2530 2890 2750 3110) @@ -355,55 +355,55 @@ circuit(RINGO rect(metal1 23580 2420 23940 3180) rect(metal2_lbl -1 2799 1 2801) ) - net($I19 + net($I19 5 rect(diff_cont 690 2890 910 3110) rect(diff_cont 690 2490 910 2710) rect(diff_cont 690 90 910 310) rect(diff_cont 690 -310 910 -90) ) - net($I8 + net($I8 6 rect(diff_cont 21810 2890 22030 3110) rect(diff_cont 21810 2490 22030 2710) rect(diff_cont 21810 90 22030 310) rect(diff_cont 21810 -310 22030 -90) ) - net($I7 + net($I7 7 rect(diff_cont 19170 2890 19390 3110) rect(diff_cont 19170 2490 19390 2710) rect(diff_cont 19170 90 19390 310) rect(diff_cont 19170 -310 19390 -90) ) - net($I6 + net($I6 8 rect(diff_cont 16530 2890 16750 3110) rect(diff_cont 16530 2490 16750 2710) rect(diff_cont 16530 90 16750 310) rect(diff_cont 16530 -310 16750 -90) ) - net($I5 + net($I5 9 rect(diff_cont 13890 2890 14110 3110) rect(diff_cont 13890 2490 14110 2710) rect(diff_cont 13890 90 14110 310) rect(diff_cont 13890 -310 14110 -90) ) - net($I4 + net($I4 10 rect(diff_cont 11250 2890 11470 3110) rect(diff_cont 11250 2490 11470 2710) rect(diff_cont 11250 90 11470 310) rect(diff_cont 11250 -310 11470 -90) ) - net($I3 + net($I3 11 rect(diff_cont 8610 2890 8830 3110) rect(diff_cont 8610 2490 8830 2710) rect(diff_cont 8610 90 8830 310) rect(diff_cont 8610 -310 8830 -90) ) - net($I2 + net($I2 12 rect(diff_cont 5970 2890 6190 3110) rect(diff_cont 5970 2490 6190 2710) rect(diff_cont 5970 90 6190 310) rect(diff_cont 5970 -310 6190 -90) ) - net($I1 + net($I1 13 rect(diff_cont 3330 2890 3550 3110) rect(diff_cont 3330 2490 3550 2710) rect(diff_cont 3330 90 3550 310) @@ -411,72 +411,72 @@ circuit(RINGO ) # Outgoing pins and their connections to nets - pin(FB FB) - pin(OSC OSC) - pin(VSS VSS) - pin(VDD VDD) + pin(FB 1) + pin(OSC 2) + pin(VSS 3) + pin(VDD 4) # Subcircuits and their connections circuit($1 INV2 location(23760 0) - pin(IN $I8) - pin($1 FB) - pin(OUT OSC) - pin($3 VSS) - pin($4 VDD) + pin(IN 6) + pin($1 1) + pin(OUT 2) + pin($3 3) + pin($4 4) ) circuit($2 INV2 location(0 0) - pin(IN FB) - pin(OUT $I19) - pin($3 VSS) - pin($4 VDD) + pin(IN 1) + pin(OUT 5) + pin($3 3) + pin($4 4) ) circuit($3 INV2 location(2640 0) - pin(IN $I19) - pin(OUT $I1) - pin($3 VSS) - pin($4 VDD) + pin(IN 5) + pin(OUT 13) + pin($3 3) + pin($4 4) ) circuit($4 INV2 location(5280 0) - pin(IN $I1) - pin(OUT $I2) - pin($3 VSS) - pin($4 VDD) + pin(IN 13) + pin(OUT 12) + pin($3 3) + pin($4 4) ) circuit($5 INV2 location(7920 0) - pin(IN $I2) - pin(OUT $I3) - pin($3 VSS) - pin($4 VDD) + pin(IN 12) + pin(OUT 11) + pin($3 3) + pin($4 4) ) circuit($6 INV2 location(10560 0) - pin(IN $I3) - pin(OUT $I4) - pin($3 VSS) - pin($4 VDD) + pin(IN 11) + pin(OUT 10) + pin($3 3) + pin($4 4) ) circuit($7 INV2 location(13200 0) - pin(IN $I4) - pin(OUT $I5) - pin($3 VSS) - pin($4 VDD) + pin(IN 10) + pin(OUT 9) + pin($3 3) + pin($4 4) ) circuit($8 INV2 location(15840 0) - pin(IN $I5) - pin(OUT $I6) - pin($3 VSS) - pin($4 VDD) + pin(IN 9) + pin(OUT 8) + pin($3 3) + pin($4 4) ) circuit($9 INV2 location(18480 0) - pin(IN $I6) - pin(OUT $I7) - pin($3 VSS) - pin($4 VDD) + pin(IN 8) + pin(OUT 7) + pin($3 3) + pin($4 4) ) circuit($10 INV2 location(21120 0) - pin(IN $I7) - pin(OUT $I8) - pin($3 VSS) - pin($4 VDD) + pin(IN 7) + pin(OUT 6) + pin($3 3) + pin($4 4) ) ) diff --git a/testdata/algo/l2n_writer_au_2.txt b/testdata/algo/l2n_writer_au_2.txt index 847a910a8..1dcc6a6a7 100644 --- a/testdata/algo/l2n_writer_au_2.txt +++ b/testdata/algo/l2n_writer_au_2.txt @@ -109,12 +109,12 @@ device(D$NMOS$1 NMOS circuit(INV2 # Nets with their geometries - net($1 + net($1 1 rect(nwell -1400 1800 1400 4580) rect(diff_cont -110 3930 110 4150) rect(ntie -400 3700 400 4380) ) - net(IN + net(IN 2 rect(poly -525 -250 -275 2250) rect(poly -1700 1620 -400 1980) rect(poly -525 -800 -275 800) @@ -122,7 +122,7 @@ circuit(INV2 rect(poly_lbl -801 1799 -799 1801) rect(poly_cont -1630 1690 -1410 1910) ) - net($3 + net($3 3 rect(poly 275 -250 525 2250) rect(poly 220 820 580 1180) rect(poly 275 2000 525 3600) @@ -139,19 +139,19 @@ circuit(INV2 rect(psd -1050 2325 -525 3275) rect(nsd -1050 -475 -525 475) ) - net(OUT + net(OUT 4 rect(diff_cont 690 2890 910 3110) rect(diff_cont 690 2490 910 2710) rect(diff_cont 690 90 910 310) rect(diff_cont 690 -310 910 -90) - polygon(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20) + polygon(metal1 800 20 * 380 940 * * 1620 620 * * 2420 980 * * 1980 1300 * * 20) rect(metal1 620 2420 980 3180) rect(metal1 620 -380 980 380) rect(metal1_lbl 799 1799 801 1801) rect(psd 525 2325 1050 3275) rect(nsd 525 -475 1050 475) ) - net(VSS + net(VSS 5 rect(diff_cont -110 -310 110 -90) rect(diff_cont -110 90 110 310) rect(diff_cont -110 90 110 310) @@ -164,7 +164,7 @@ circuit(INV2 rect(metal2_lbl 1239 -91 1241 -89) rect(nsd -275 -475 275 475) ) - net(VDD + net(VDD 6 rect(diff_cont -110 2490 110 2710) rect(diff_cont -110 2890 110 3110) rect(diff_cont -110 2890 110 3110) @@ -177,19 +177,19 @@ circuit(INV2 rect(metal2_lbl 1249 2799 1251 2801) rect(psd -275 2325 275 3275) ) - net(BULK + net(BULK 7 rect(diff_cont -110 -1360 110 -1140) rect(ptie -400 -1590 400 -910) ) # Outgoing pins and their connections to nets - pin($0 $1) - pin(IN IN) - pin($2 $3) - pin(OUT OUT) - pin(VSS VSS) - pin(VDD VDD) - pin(BULK BULK) + pin($0 1) + pin(IN 2) + pin($2 3) + pin(OUT 4) + pin(VSS 5) + pin(VDD 6) + pin(BULK 7) # Devices and their connections device($1 D$PMOS @@ -200,10 +200,10 @@ circuit(INV2 param(AD 0.26125) param(PS 2.95) param(PD 1.5) - terminal(S $3) - terminal(G IN) - terminal(D VDD) - terminal(B $1) + terminal(S 3) + terminal(G 2) + terminal(D 6) + terminal(B 1) ) device($2 D$PMOS$1 location(400 2800) @@ -213,10 +213,10 @@ circuit(INV2 param(AD 0.49875) param(PS 1.5) param(PD 2.95) - terminal(S VDD) - terminal(G $3) - terminal(D OUT) - terminal(B $1) + terminal(S 6) + terminal(G 3) + terminal(D 4) + terminal(B 1) ) device($3 D$NMOS location(-400 0) @@ -226,10 +226,10 @@ circuit(INV2 param(AD 0.26125) param(PS 2.95) param(PD 1.5) - terminal(S $3) - terminal(G IN) - terminal(D VSS) - terminal(B BULK) + terminal(S 3) + terminal(G 2) + terminal(D 5) + terminal(B 7) ) device($4 D$NMOS$1 location(400 0) @@ -239,24 +239,24 @@ circuit(INV2 param(AD 0.49875) param(PS 1.5) param(PD 2.95) - terminal(S VSS) - terminal(G $3) - terminal(D OUT) - terminal(B BULK) + terminal(S 5) + terminal(G 3) + terminal(D 4) + terminal(B 7) ) ) circuit(INV2PAIR # Nets with their geometries - net(BULK) - net($I8 + net(BULK 1) + net($I8 2 rect(diff_cont 3430 3290 3650 3510) rect(diff_cont 3430 3690 3650 3910) rect(diff_cont 3430 490 3650 710) rect(diff_cont 3430 890 3650 1110) ) - net($I6 + net($I6 3 rect(diff_cont 4230 3290 4450 3510) rect(diff_cont 4230 3690 4450 3910) rect(diff_cont 4230 3690 4450 3910) @@ -270,7 +270,7 @@ circuit(INV2PAIR rect(metal1 1520 3220 1880 3980) rect(metal1 1520 3220 1880 3980) ) - net($I5 + net($I5 4 rect(diff_cont 4230 490 4450 710) rect(diff_cont 4230 890 4450 1110) rect(diff_cont 4230 890 4450 1110) @@ -284,54 +284,54 @@ circuit(INV2PAIR rect(metal1 1520 420 1880 1180) rect(metal1 1520 420 1880 1180) ) - net($I4 + net($I4 5 rect(diff_cont 2390 3690 2610 3910) rect(diff_cont 2390 3290 2610 3510) rect(diff_cont 2390 890 2610 1110) rect(diff_cont 2390 490 2610 710) ) - net($I3) - net($I2 + net($I3 6) + net($I2 7 rect(diff_cont 5030 3690 5250 3910) rect(diff_cont 5030 3290 5250 3510) rect(diff_cont 5030 890 5250 1110) rect(diff_cont 5030 490 5250 710) ) - net($I1) + net($I1 8) # Outgoing pins and their connections to nets - pin(BULK BULK) - pin($1 $I8) - pin($2 $I6) - pin($3 $I5) - pin($4 $I3) - pin($5 $I2) - pin($6 $I1) + pin(BULK 1) + pin($1 2) + pin($2 3) + pin($3 4) + pin($4 6) + pin($5 7) + pin($6 8) # Subcircuits and their connections circuit($1 INV2 location(1700 800) - pin($0 $I1) - pin(IN $I3) - pin(OUT $I4) - pin(VSS $I5) - pin(VDD $I6) - pin(BULK BULK) + pin($0 8) + pin(IN 6) + pin(OUT 5) + pin(VSS 4) + pin(VDD 3) + pin(BULK 1) ) circuit($2 INV2 location(4340 800) - pin($0 $I1) - pin(IN $I4) - pin($2 $I8) - pin(OUT $I2) - pin(VSS $I5) - pin(VDD $I6) - pin(BULK BULK) + pin($0 8) + pin(IN 5) + pin($2 2) + pin(OUT 7) + pin(VSS 4) + pin(VDD 3) + pin(BULK 1) ) ) circuit(RINGO # Nets with their geometries - net(FB + net(FB 1 rect(diff_cont 22850 2490 23070 2710) rect(diff_cont 22850 2890 23070 3110) rect(diff_cont 22850 -310 23070 -90) @@ -342,7 +342,7 @@ circuit(RINGO rect(metal2 -1720 1600 23160 2000) rect(metal2_lbl -1 1799 1 1801) ) - net(OSC + net(OSC 2 rect(diff_cont 24450 2890 24670 3110) rect(diff_cont 24450 2490 24670 2710) rect(diff_cont 24450 90 24670 310) @@ -351,7 +351,7 @@ circuit(RINGO rect(metal2 24360 1600 24760 2000) rect(metal2_lbl 24559 1799 24561 1801) ) - net(VDD + net(VDD 3 rect(diff_cont 7810 2490 8030 2710) rect(diff_cont 7810 2890 8030 3110) rect(diff_cont 7810 2890 8030 3110) @@ -424,7 +424,7 @@ circuit(RINGO rect(metal1 20940 2420 21300 3180) rect(metal2_lbl -1 2799 1 2801) ) - net('BULK,VSS' + net('BULK,VSS' 4 rect(diff_cont 7810 -310 8030 -90) rect(diff_cont 7810 90 8030 310) rect(diff_cont 7810 90 8030 310) @@ -497,25 +497,25 @@ circuit(RINGO rect(metal1 20940 -380 21300 380) rect(metal2_lbl -1 -1 1 1) ) - net($I13 + net($I13 5 rect(diff_cont 3330 2890 3550 3110) rect(diff_cont 3330 2490 3550 2710) rect(diff_cont 3330 90 3550 310) rect(diff_cont 3330 -310 3550 -90) ) - net($I7 + net($I7 6 rect(diff_cont 19170 2890 19390 3110) rect(diff_cont 19170 2490 19390 2710) rect(diff_cont 19170 90 19390 310) rect(diff_cont 19170 -310 19390 -90) ) - net($I6 + net($I6 7 rect(diff_cont 13890 2890 14110 3110) rect(diff_cont 13890 2490 14110 2710) rect(diff_cont 13890 90 14110 310) rect(diff_cont 13890 -310 14110 -90) ) - net($I5 + net($I5 8 rect(diff_cont 8610 2890 8830 3110) rect(diff_cont 8610 2490 8830 2710) rect(diff_cont 8610 90 8830 310) @@ -523,52 +523,52 @@ circuit(RINGO ) # Outgoing pins and their connections to nets - pin(FB FB) - pin(OSC OSC) - pin(VDD VDD) - pin('BULK,VSS' 'BULK,VSS') + pin(FB 1) + pin(OSC 2) + pin(VDD 3) + pin('BULK,VSS' 4) # Subcircuits and their connections circuit($1 INV2PAIR location(19420 -800) - pin(BULK 'BULK,VSS') - pin($1 FB) - pin($2 VDD) - pin($3 'BULK,VSS') - pin($4 $I7) - pin($5 OSC) - pin($6 VDD) + pin(BULK 4) + pin($1 1) + pin($2 3) + pin($3 4) + pin($4 6) + pin($5 2) + pin($6 3) ) circuit($2 INV2PAIR location(-1700 -800) - pin(BULK 'BULK,VSS') - pin($2 VDD) - pin($3 'BULK,VSS') - pin($4 FB) - pin($5 $I13) - pin($6 VDD) + pin(BULK 4) + pin($2 3) + pin($3 4) + pin($4 1) + pin($5 5) + pin($6 3) ) circuit($3 INV2PAIR location(3580 -800) - pin(BULK 'BULK,VSS') - pin($2 VDD) - pin($3 'BULK,VSS') - pin($4 $I13) - pin($5 $I5) - pin($6 VDD) + pin(BULK 4) + pin($2 3) + pin($3 4) + pin($4 5) + pin($5 8) + pin($6 3) ) circuit($4 INV2PAIR location(8860 -800) - pin(BULK 'BULK,VSS') - pin($2 VDD) - pin($3 'BULK,VSS') - pin($4 $I5) - pin($5 $I6) - pin($6 VDD) + pin(BULK 4) + pin($2 3) + pin($3 4) + pin($4 8) + pin($5 7) + pin($6 3) ) circuit($5 INV2PAIR location(14140 -800) - pin(BULK 'BULK,VSS') - pin($2 VDD) - pin($3 'BULK,VSS') - pin($4 $I6) - pin($5 $I7) - pin($6 VDD) + pin(BULK 4) + pin($2 3) + pin($3 4) + pin($4 7) + pin($5 6) + pin($6 3) ) ) diff --git a/testdata/algo/l2n_writer_au_2s.txt b/testdata/algo/l2n_writer_au_2s.txt index 034f62c26..5d7f46129 100644 --- a/testdata/algo/l2n_writer_au_2s.txt +++ b/testdata/algo/l2n_writer_au_2s.txt @@ -89,12 +89,12 @@ D(D$NMOS$1 NMOS ) ) X(INV2 - N($1 + N($1 1 R(nwell -1400 1800 1400 4580) R(diff_cont -110 3930 110 4150) R(ntie -400 3700 400 4380) ) - N(IN + N(IN 2 R(poly -525 -250 -275 2250) R(poly -1700 1620 -400 1980) R(poly -525 -800 -275 800) @@ -102,7 +102,7 @@ X(INV2 R(poly_lbl -801 1799 -799 1801) R(poly_cont -1630 1690 -1410 1910) ) - N($3 + N($3 3 R(poly 275 -250 525 2250) R(poly 220 820 580 1180) R(poly 275 2000 525 3600) @@ -119,19 +119,19 @@ X(INV2 R(psd -1050 2325 -525 3275) R(nsd -1050 -475 -525 475) ) - N(OUT + N(OUT 4 R(diff_cont 690 2890 910 3110) R(diff_cont 690 2490 910 2710) R(diff_cont 690 90 910 310) R(diff_cont 690 -310 910 -90) - Q(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20) + Q(metal1 800 20 * 380 940 * * 1620 620 * * 2420 980 * * 1980 1300 * * 20) R(metal1 620 2420 980 3180) R(metal1 620 -380 980 380) R(metal1_lbl 799 1799 801 1801) R(psd 525 2325 1050 3275) R(nsd 525 -475 1050 475) ) - N(VSS + N(VSS 5 R(diff_cont -110 -310 110 -90) R(diff_cont -110 90 110 310) R(diff_cont -110 90 110 310) @@ -144,7 +144,7 @@ X(INV2 R(metal2_lbl 1239 -91 1241 -89) R(nsd -275 -475 275 475) ) - N(VDD + N(VDD 6 R(diff_cont -110 2490 110 2710) R(diff_cont -110 2890 110 3110) R(diff_cont -110 2890 110 3110) @@ -157,17 +157,17 @@ X(INV2 R(metal2_lbl 1249 2799 1251 2801) R(psd -275 2325 275 3275) ) - N(BULK + N(BULK 7 R(diff_cont -110 -1360 110 -1140) R(ptie -400 -1590 400 -910) ) - P($0 $1) - P(IN IN) - P($2 $3) - P(OUT OUT) - P(VSS VSS) - P(VDD VDD) - P(BULK BULK) + P($0 1) + P(IN 2) + P($2 3) + P(OUT 4) + P(VSS 5) + P(VDD 6) + P(BULK 7) D($1 D$PMOS Y(-400 2800) E(L 0.25) @@ -176,10 +176,10 @@ X(INV2 E(AD 0.26125) E(PS 2.95) E(PD 1.5) - T(S $3) - T(G IN) - T(D VDD) - T(B $1) + T(S 3) + T(G 2) + T(D 6) + T(B 1) ) D($2 D$PMOS$1 Y(400 2800) @@ -189,10 +189,10 @@ X(INV2 E(AD 0.49875) E(PS 1.5) E(PD 2.95) - T(S VDD) - T(G $3) - T(D OUT) - T(B $1) + T(S 6) + T(G 3) + T(D 4) + T(B 1) ) D($3 D$NMOS Y(-400 0) @@ -202,10 +202,10 @@ X(INV2 E(AD 0.26125) E(PS 2.95) E(PD 1.5) - T(S $3) - T(G IN) - T(D VSS) - T(B BULK) + T(S 3) + T(G 2) + T(D 5) + T(B 7) ) D($4 D$NMOS$1 Y(400 0) @@ -215,21 +215,21 @@ X(INV2 E(AD 0.49875) E(PS 1.5) E(PD 2.95) - T(S VSS) - T(G $3) - T(D OUT) - T(B BULK) + T(S 5) + T(G 3) + T(D 4) + T(B 7) ) ) X(INV2PAIR - N(BULK) - N($I8 + N(BULK 1) + N($I8 2 R(diff_cont 3430 3290 3650 3510) R(diff_cont 3430 3690 3650 3910) R(diff_cont 3430 490 3650 710) R(diff_cont 3430 890 3650 1110) ) - N($I6 + N($I6 3 R(diff_cont 4230 3290 4450 3510) R(diff_cont 4230 3690 4450 3910) R(diff_cont 4230 3690 4450 3910) @@ -243,7 +243,7 @@ X(INV2PAIR R(metal1 1520 3220 1880 3980) R(metal1 1520 3220 1880 3980) ) - N($I5 + N($I5 4 R(diff_cont 4230 490 4450 710) R(diff_cont 4230 890 4450 1110) R(diff_cont 4230 890 4450 1110) @@ -257,47 +257,47 @@ X(INV2PAIR R(metal1 1520 420 1880 1180) R(metal1 1520 420 1880 1180) ) - N($I4 + N($I4 5 R(diff_cont 2390 3690 2610 3910) R(diff_cont 2390 3290 2610 3510) R(diff_cont 2390 890 2610 1110) R(diff_cont 2390 490 2610 710) ) - N($I3) - N($I2 + N($I3 6) + N($I2 7 R(diff_cont 5030 3690 5250 3910) R(diff_cont 5030 3290 5250 3510) R(diff_cont 5030 890 5250 1110) R(diff_cont 5030 490 5250 710) ) - N($I1) - P(BULK BULK) - P($1 $I8) - P($2 $I6) - P($3 $I5) - P($4 $I3) - P($5 $I2) - P($6 $I1) + N($I1 8) + P(BULK 1) + P($1 2) + P($2 3) + P($3 4) + P($4 6) + P($5 7) + P($6 8) X($1 INV2 Y(1700 800) - P($0 $I1) - P(IN $I3) - P(OUT $I4) - P(VSS $I5) - P(VDD $I6) - P(BULK BULK) + P($0 8) + P(IN 6) + P(OUT 5) + P(VSS 4) + P(VDD 3) + P(BULK 1) ) X($2 INV2 Y(4340 800) - P($0 $I1) - P(IN $I4) - P($2 $I8) - P(OUT $I2) - P(VSS $I5) - P(VDD $I6) - P(BULK BULK) + P($0 8) + P(IN 5) + P($2 2) + P(OUT 7) + P(VSS 4) + P(VDD 3) + P(BULK 1) ) ) X(RINGO - N(FB + N(FB 1 R(diff_cont 22850 2490 23070 2710) R(diff_cont 22850 2890 23070 3110) R(diff_cont 22850 -310 23070 -90) @@ -308,7 +308,7 @@ X(RINGO R(metal2 -1720 1600 23160 2000) R(metal2_lbl -1 1799 1 1801) ) - N(OSC + N(OSC 2 R(diff_cont 24450 2890 24670 3110) R(diff_cont 24450 2490 24670 2710) R(diff_cont 24450 90 24670 310) @@ -317,7 +317,7 @@ X(RINGO R(metal2 24360 1600 24760 2000) R(metal2_lbl 24559 1799 24561 1801) ) - N(VDD + N(VDD 3 R(diff_cont 7810 2490 8030 2710) R(diff_cont 7810 2890 8030 3110) R(diff_cont 7810 2890 8030 3110) @@ -390,7 +390,7 @@ X(RINGO R(metal1 20940 2420 21300 3180) R(metal2_lbl -1 2799 1 2801) ) - N('BULK,VSS' + N('BULK,VSS' 4 R(diff_cont 7810 -310 8030 -90) R(diff_cont 7810 90 8030 310) R(diff_cont 7810 90 8030 310) @@ -463,73 +463,73 @@ X(RINGO R(metal1 20940 -380 21300 380) R(metal2_lbl -1 -1 1 1) ) - N($I13 + N($I13 5 R(diff_cont 3330 2890 3550 3110) R(diff_cont 3330 2490 3550 2710) R(diff_cont 3330 90 3550 310) R(diff_cont 3330 -310 3550 -90) ) - N($I7 + N($I7 6 R(diff_cont 19170 2890 19390 3110) R(diff_cont 19170 2490 19390 2710) R(diff_cont 19170 90 19390 310) R(diff_cont 19170 -310 19390 -90) ) - N($I6 + N($I6 7 R(diff_cont 13890 2890 14110 3110) R(diff_cont 13890 2490 14110 2710) R(diff_cont 13890 90 14110 310) R(diff_cont 13890 -310 14110 -90) ) - N($I5 + N($I5 8 R(diff_cont 8610 2890 8830 3110) R(diff_cont 8610 2490 8830 2710) R(diff_cont 8610 90 8830 310) R(diff_cont 8610 -310 8830 -90) ) - P(FB FB) - P(OSC OSC) - P(VDD VDD) - P('BULK,VSS' 'BULK,VSS') + P(FB 1) + P(OSC 2) + P(VDD 3) + P('BULK,VSS' 4) X($1 INV2PAIR Y(19420 -800) - P(BULK 'BULK,VSS') - P($1 FB) - P($2 VDD) - P($3 'BULK,VSS') - P($4 $I7) - P($5 OSC) - P($6 VDD) + P(BULK 4) + P($1 1) + P($2 3) + P($3 4) + P($4 6) + P($5 2) + P($6 3) ) X($2 INV2PAIR Y(-1700 -800) - P(BULK 'BULK,VSS') - P($2 VDD) - P($3 'BULK,VSS') - P($4 FB) - P($5 $I13) - P($6 VDD) + P(BULK 4) + P($2 3) + P($3 4) + P($4 1) + P($5 5) + P($6 3) ) X($3 INV2PAIR Y(3580 -800) - P(BULK 'BULK,VSS') - P($2 VDD) - P($3 'BULK,VSS') - P($4 $I13) - P($5 $I5) - P($6 VDD) + P(BULK 4) + P($2 3) + P($3 4) + P($4 5) + P($5 8) + P($6 3) ) X($4 INV2PAIR Y(8860 -800) - P(BULK 'BULK,VSS') - P($2 VDD) - P($3 'BULK,VSS') - P($4 $I5) - P($5 $I6) - P($6 VDD) + P(BULK 4) + P($2 3) + P($3 4) + P($4 8) + P($5 7) + P($6 3) ) X($5 INV2PAIR Y(14140 -800) - P(BULK 'BULK,VSS') - P($2 VDD) - P($3 'BULK,VSS') - P($4 $I6) - P($5 $I7) - P($6 VDD) + P(BULK 4) + P($2 3) + P($3 4) + P($4 7) + P($5 6) + P($6 3) ) ) diff --git a/testdata/algo/l2n_writer_au_s.txt b/testdata/algo/l2n_writer_au_s.txt index 726395991..6be18f125 100644 --- a/testdata/algo/l2n_writer_au_s.txt +++ b/testdata/algo/l2n_writer_au_s.txt @@ -68,7 +68,7 @@ D(D$NMOS$1 NMOS ) ) X(INV2 - N(IN + N(IN 1 R(poly -525 -250 -275 2250) R(poly -1700 1620 -400 1980) R(poly -525 -800 -275 800) @@ -76,7 +76,7 @@ X(INV2 R(poly_lbl -801 1799 -799 1801) R(poly_cont -1630 1690 -1410 1910) ) - N($2 + N($2 2 R(poly 275 -250 525 2250) R(poly 220 820 580 1180) R(poly 275 2000 525 3600) @@ -93,19 +93,19 @@ X(INV2 R(psd -1050 2325 -525 3275) R(nsd -1050 -475 -525 475) ) - N(OUT + N(OUT 3 R(diff_cont 690 2890 910 3110) R(diff_cont 690 2490 910 2710) R(diff_cont 690 90 910 310) R(diff_cont 690 -310 910 -90) - Q(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20) + Q(metal1 800 20 * 380 940 * * 1620 620 * * 2420 980 * * 1980 1300 * * 20) R(metal1 620 2420 980 3180) R(metal1 620 -380 980 380) R(metal1_lbl 799 1799 801 1801) R(psd 525 2325 1050 3275) R(nsd 525 -475 1050 475) ) - N($4 + N($4 4 R(diff_cont -110 -310 110 -90) R(diff_cont -110 90 110 310) R(diff_cont -110 90 110 310) @@ -117,7 +117,7 @@ X(INV2 R(metal2 -1400 -450 1400 450) R(nsd -275 -475 275 475) ) - N($5 + N($5 5 R(diff_cont -110 2490 110 2710) R(diff_cont -110 2890 110 3110) R(diff_cont -110 2890 110 3110) @@ -129,11 +129,11 @@ X(INV2 R(metal2 -1400 2350 1400 3250) R(psd -275 2325 275 3275) ) - P(IN IN) - P($1 $2) - P(OUT OUT) - P($3 $4) - P($4 $5) + P(IN 1) + P($1 2) + P(OUT 3) + P($3 4) + P($4 5) D($1 D$PMOS Y(-400 2800) E(L 0.25) @@ -142,9 +142,9 @@ X(INV2 E(AD 0.26125) E(PS 2.95) E(PD 1.5) - T(S $2) - T(G IN) - T(D $5) + T(S 2) + T(G 1) + T(D 5) ) D($2 D$PMOS$1 Y(400 2800) @@ -154,9 +154,9 @@ X(INV2 E(AD 0.49875) E(PS 1.5) E(PD 2.95) - T(S $5) - T(G $2) - T(D OUT) + T(S 5) + T(G 2) + T(D 3) ) D($3 D$NMOS Y(-400 0) @@ -166,9 +166,9 @@ X(INV2 E(AD 0.26125) E(PS 2.95) E(PD 1.5) - T(S $2) - T(G IN) - T(D $4) + T(S 2) + T(G 1) + T(D 4) ) D($4 D$NMOS$1 Y(400 0) @@ -178,13 +178,13 @@ X(INV2 E(AD 0.49875) E(PS 1.5) E(PD 2.95) - T(S $4) - T(G $2) - T(D OUT) + T(S 4) + T(G 2) + T(D 3) ) ) X(RINGO - N(FB + N(FB 1 R(diff_cont 22850 2490 23070 2710) R(diff_cont 22850 2890 23070 3110) R(diff_cont 22850 -310 23070 -90) @@ -195,7 +195,7 @@ X(RINGO R(metal2 -1720 1600 23160 2000) R(metal2_lbl -1 1799 1 1801) ) - N(OSC + N(OSC 2 R(diff_cont 24450 2890 24670 3110) R(diff_cont 24450 2490 24670 2710) R(diff_cont 24450 90 24670 310) @@ -204,7 +204,7 @@ X(RINGO R(metal2 24360 1600 24760 2000) R(metal2_lbl 24559 1799 24561 1801) ) - N(VSS + N(VSS 3 R(diff_cont 2530 -310 2750 -90) R(diff_cont 2530 90 2750 310) R(diff_cont 2530 90 2750 310) @@ -267,7 +267,7 @@ X(RINGO R(metal1 23580 -380 23940 380) R(metal2_lbl -1 -1 1 1) ) - N(VDD + N(VDD 4 R(diff_cont 2530 2490 2750 2710) R(diff_cont 2530 2890 2750 3110) R(diff_cont 2530 2890 2750 3110) @@ -330,123 +330,123 @@ X(RINGO R(metal1 23580 2420 23940 3180) R(metal2_lbl -1 2799 1 2801) ) - N($I19 + N($I19 5 R(diff_cont 690 2890 910 3110) R(diff_cont 690 2490 910 2710) R(diff_cont 690 90 910 310) R(diff_cont 690 -310 910 -90) ) - N($I8 + N($I8 6 R(diff_cont 21810 2890 22030 3110) R(diff_cont 21810 2490 22030 2710) R(diff_cont 21810 90 22030 310) R(diff_cont 21810 -310 22030 -90) ) - N($I7 + N($I7 7 R(diff_cont 19170 2890 19390 3110) R(diff_cont 19170 2490 19390 2710) R(diff_cont 19170 90 19390 310) R(diff_cont 19170 -310 19390 -90) ) - N($I6 + N($I6 8 R(diff_cont 16530 2890 16750 3110) R(diff_cont 16530 2490 16750 2710) R(diff_cont 16530 90 16750 310) R(diff_cont 16530 -310 16750 -90) ) - N($I5 + N($I5 9 R(diff_cont 13890 2890 14110 3110) R(diff_cont 13890 2490 14110 2710) R(diff_cont 13890 90 14110 310) R(diff_cont 13890 -310 14110 -90) ) - N($I4 + N($I4 10 R(diff_cont 11250 2890 11470 3110) R(diff_cont 11250 2490 11470 2710) R(diff_cont 11250 90 11470 310) R(diff_cont 11250 -310 11470 -90) ) - N($I3 + N($I3 11 R(diff_cont 8610 2890 8830 3110) R(diff_cont 8610 2490 8830 2710) R(diff_cont 8610 90 8830 310) R(diff_cont 8610 -310 8830 -90) ) - N($I2 + N($I2 12 R(diff_cont 5970 2890 6190 3110) R(diff_cont 5970 2490 6190 2710) R(diff_cont 5970 90 6190 310) R(diff_cont 5970 -310 6190 -90) ) - N($I1 + N($I1 13 R(diff_cont 3330 2890 3550 3110) R(diff_cont 3330 2490 3550 2710) R(diff_cont 3330 90 3550 310) R(diff_cont 3330 -310 3550 -90) ) - P(FB FB) - P(OSC OSC) - P(VSS VSS) - P(VDD VDD) + P(FB 1) + P(OSC 2) + P(VSS 3) + P(VDD 4) X($1 INV2 Y(23760 0) - P(IN $I8) - P($1 FB) - P(OUT OSC) - P($3 VSS) - P($4 VDD) + P(IN 6) + P($1 1) + P(OUT 2) + P($3 3) + P($4 4) ) X($2 INV2 Y(0 0) - P(IN FB) - P(OUT $I19) - P($3 VSS) - P($4 VDD) + P(IN 1) + P(OUT 5) + P($3 3) + P($4 4) ) X($3 INV2 Y(2640 0) - P(IN $I19) - P(OUT $I1) - P($3 VSS) - P($4 VDD) + P(IN 5) + P(OUT 13) + P($3 3) + P($4 4) ) X($4 INV2 Y(5280 0) - P(IN $I1) - P(OUT $I2) - P($3 VSS) - P($4 VDD) + P(IN 13) + P(OUT 12) + P($3 3) + P($4 4) ) X($5 INV2 Y(7920 0) - P(IN $I2) - P(OUT $I3) - P($3 VSS) - P($4 VDD) + P(IN 12) + P(OUT 11) + P($3 3) + P($4 4) ) X($6 INV2 Y(10560 0) - P(IN $I3) - P(OUT $I4) - P($3 VSS) - P($4 VDD) + P(IN 11) + P(OUT 10) + P($3 3) + P($4 4) ) X($7 INV2 Y(13200 0) - P(IN $I4) - P(OUT $I5) - P($3 VSS) - P($4 VDD) + P(IN 10) + P(OUT 9) + P($3 3) + P($4 4) ) X($8 INV2 Y(15840 0) - P(IN $I5) - P(OUT $I6) - P($3 VSS) - P($4 VDD) + P(IN 9) + P(OUT 8) + P($3 3) + P($4 4) ) X($9 INV2 Y(18480 0) - P(IN $I6) - P(OUT $I7) - P($3 VSS) - P($4 VDD) + P(IN 8) + P(OUT 7) + P($3 3) + P($4 4) ) X($10 INV2 Y(21120 0) - P(IN $I7) - P(OUT $I8) - P($3 VSS) - P($4 VDD) + P(IN 7) + P(OUT 6) + P($3 3) + P($4 4) ) ) From 3f1cd226a525c7fcc184a2e1e57dd540430c1e11 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 3 Feb 2019 13:33:58 +0100 Subject: [PATCH 236/335] Made net name optional in l2n format. --- src/db/db/dbLayoutToNetlistFormatDefs.cc | 2 + src/db/db/dbLayoutToNetlistFormatDefs.h | 8 +++- src/db/db/dbLayoutToNetlistReader.cc | 10 ++-- src/db/db/dbLayoutToNetlistWriter.cc | 14 +++++- src/db/db/dbNetlistDeviceExtractorClasses.cc | 4 +- testdata/algo/l2n_reader_au.gds | Bin 18382 -> 18006 bytes testdata/algo/l2n_reader_au_2.gds | Bin 27844 -> 25788 bytes testdata/algo/l2n_writer_au.txt | 36 +++++++-------- testdata/algo/l2n_writer_au_2.txt | 46 +++++++++---------- testdata/algo/l2n_writer_au_2s.txt | 46 +++++++++---------- testdata/algo/l2n_writer_au_s.txt | 36 +++++++-------- 11 files changed, 112 insertions(+), 90 deletions(-) diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.cc b/src/db/db/dbLayoutToNetlistFormatDefs.cc index 572fdccb8..3a00f45e0 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.cc +++ b/src/db/db/dbLayoutToNetlistFormatDefs.cc @@ -36,6 +36,7 @@ namespace l2n_std_format template<> DB_PUBLIC const std::string keys::global_key ("global"); template<> DB_PUBLIC const std::string keys::circuit_key ("circuit"); template<> DB_PUBLIC const std::string keys::net_key ("net"); + template<> DB_PUBLIC const std::string keys::name_key ("name"); template<> DB_PUBLIC const std::string keys::device_key ("device"); template<> DB_PUBLIC const std::string keys::polygon_key ("polygon"); template<> DB_PUBLIC const std::string keys::rect_key ("rect"); @@ -57,6 +58,7 @@ namespace l2n_std_format template<> DB_PUBLIC const std::string keys::global_key ("G"); template<> DB_PUBLIC const std::string keys::circuit_key ("X"); template<> DB_PUBLIC const std::string keys::net_key ("N"); + template<> DB_PUBLIC const std::string keys::name_key ("I"); template<> DB_PUBLIC const std::string keys::device_key ("D"); template<> DB_PUBLIC const std::string keys::polygon_key ("Q"); template<> DB_PUBLIC const std::string keys::rect_key ("R"); diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index 663ca16c5..8bb38dbec 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -63,7 +63,7 @@ namespace db * * [circuit-def]: * - * net( [geometry-def]) + * net( [net-name]? [geometry-def]*) * - net geometry [short key: N] * A net declaration shall be there also if no geometry * is present. The ID is a numerical shortcut for the net. @@ -72,6 +72,9 @@ namespace db * - device with connections [short key: D] * circuit( [circuit-def]) - subcircuit with connections [short key: X] * + * [net-name]: + * name() - specify net name [short key: + * * [geometry-def]: * * polygon( ...) - defines a polygon [short key: Q] @@ -81,7 +84,7 @@ namespace db * * [device-abstract-def]: * - * terminal( [geometry-def]) + * terminal( [geometry-def]*) * - specifies the terminal geometry [short key: T] * * [device-def]: @@ -116,6 +119,7 @@ namespace l2n_std_format static const std::string global_key; static const std::string circuit_key; static const std::string net_key; + static const std::string name_key; static const std::string device_key; static const std::string subcircuit_key; static const std::string polygon_key; diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index 687c33a12..851549d31 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -394,10 +394,14 @@ LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit * { Brace br (this); - std::string name; - read_word_or_quoted (name); - unsigned int id = (unsigned int) read_int (); + std::string name; + + if (test (skeys::name_key) || test (lkeys::name_key)) { + Brace br_name (this); + read_word_or_quoted (name); + br_name.done (); + } db::Net *net = new db::Net (); net->set_name (name); diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 2be9f008b..9508c2fb8 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -309,7 +309,11 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net } else { if (! any) { - *mp_stream << indent1 << Keys::net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << " " << id << endl; + *mp_stream << indent1 << Keys::net_key << "(" << id; + if (! net.name ().empty ()) { + *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")"; + } + *mp_stream << endl; any = true; } @@ -330,7 +334,13 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net if (any) { *mp_stream << indent1 << ")" << endl; } else { - *mp_stream << indent1 << Keys::net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << " " << id << ")" << endl; + + *mp_stream << indent1 << Keys::net_key << "(" << id; + if (! net.name ().empty ()) { + *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")"; + } + *mp_stream << ")" << endl; + } } diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index a93c13ed6..91a22c45e 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -72,7 +72,10 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vectorNyA7+1#(3bIHheyqa6qK+1VA7BJg*2GGyt+rZ3D?35u z7J{H4*jc{L(sB`5w%9+gy@fE^`Z7isVeXDoT<&nrJ@>rBy*p%o4_F@v(6m)3GA;QJ zG|-_9a?l^{kNZj;(ZJd!0sOAzBPSp$aMQ4Goy|p0;x(%g$dE>C!%Gq==+w-SPVM2G zn)%78nS1_?a9vMO$e%T z3htYq5RE*ZWm59N+@j>aryC9in2UaTu(_yR{T&+xd6v1+30FTX8>Q7RpR6^uM>_JH zh55y0#|54QC?>SXymz_OB2wqG6_9?J&Aem>{P>_yqWZ=&9~D76x1?v#dKvH uH@t499;&K}MO77|suQ^56}jVSO@Sj1I3K{{X?caSqRgiN=)x delta 1711 zcmccC!+5TrQHg<#fr%-CL57iu{V@Y0g9rl?gARipGJB$uvM6?ms6-qR7vix?Om<TC{8JC_3%((1fVZo)RiUpURcPz0ue44A~glU3>&wewz=!;XvEPCQI{+5O5-gro18k@SQBlqq@0*ddF!m5u z#BWEsum)ZQn|p<07;zZ>PeeQ$YldUsVqZ$W?*4wV31+OnP(YzKq>=(Dpd># zsh!*)Dyo3pKAA%!Mjg#nTT z6`Fz-R!I?5Xa-ieM4F&NbFjj5G6WS`OjeYWp8QAlqauNzF|^#wC(n%|(l_fVi1TAl z51XU(aZ0t>O=rXrCBU=-iaR%_$$S!Gn}Zy_;<7o$NgbEe4CmF1%$|mplNY#(a`Om5 j6N3cG@r}{=MqoY{+&;s}wr-LDmcHtW diff --git a/testdata/algo/l2n_reader_au_2.gds b/testdata/algo/l2n_reader_au_2.gds index f34c627da51201d63bf6d2f5e2b2257897bc3bfd..eb63e79b39d338142e906dda142a392f46b6f16e 100644 GIT binary patch delta 2127 zcmaizO-vI(6vtMV-jr7*~6G!=@W3(0IuS5 z=9RFCCmB2a+zrOg3+@m*>@XCoUTWi|Ha@mZ z3$^i68^1(5N@4{RR|J;AZX&e4;That!4%{}BDo*Bf*F$UZ7Ir37(B8RzRf)_9a7|R zI8F)U=Py(Q1=*Yk#q3<;4`_^0b6snx)TyNd)Dw-U#@N7WVk;~xb_L;cGz{LDNXoGY z>wXry`lv*}D){12#sz7pN?bz;c+f)U?>2NhGI>Wbw(t~VhBQ6POihUAc$D=z!^5)9%{`$?6_)9m(^nd+dQ4Xpv}SYqszR}%#k6sf8M*6c%-h>i zl5*2JJ4=BwRB88Fh)>W+)uMG8ZA@rUu|j9FkRY!rOx(~^k}OQEuugGgx)+|$2B4!5 zXi^ZJqP@Mp*I-xeBsxzu3ogyZ&5uML&E_$$(yOJ?{leUV fEPo&y(lQNdH|D9H8KYRwj43uUH%C9h+I;vQrPo|6 delta 4310 zcmb7HOH30{6n!)OnOd~Sbb$WtwA5OQY3YZs60OF9{KOceV4*aTB`!=%j6rs+{BRd6 zAQ6pm;fBR*2r;HHE_7u#nrK)MH7<-HYU~0xIMcCwlD>DxZaVFI_uPB#=iK)B8}8Ky zEmNXtqnN-dN3&_?X*N%InoWy= zW^>m-v*C?2o0~?O&3CFz!9=rpZyKd!=d77_vTCNGwOeQ>k1bKR*~t+?Y%Wu`U3phu zHn-_S^?|@!YsA+<2k>}UHC>}5fOSVI#QKXf-Zq1pD5 z;%399%Zo7tQV_V*182jj$OA;fK>3LCN3?xHuV zsDpb@Pwv*p-On1VdKrU-PpvTzYy1e@_k?_D=7lEOY zM@0RF`7KQT5El;c;A4vy!OY>xlyl_-RVdIgyF;)Fwrh%hOu36|Hr z6-IJrQUY)R0ZFb}sIEYTQKO^)S0pUtDal2duq7#~BZMDPg<}6F7-kQjFN8>XE_9TQ za2-LA7eZ@>!}YsZnZo|l7xKYelFS7h2vc=(;_Sl8<%o+SR&&vNQvDzf{0k!070h+Q zlEB2zik89R zR~isQsrCvMlGadoM1HmLFfg?Zw|c8}f) zVQBVVJrzPw)a0Wf)hdKwfm$IHtXiRc=dcP544=}YM)Jz&0%cLbWM0x$U@^p07F9*J zwl+sp6_r2cD>Qds#&cHNq1qmXnW7hl3;z1QC*;{ehBM?8F{w+KD{<#zdhf@S lifPi;&REf7(ze-H(a$UER{6qHUA&s;Yky?7e})>38Jnz diff --git a/testdata/algo/l2n_writer_au.txt b/testdata/algo/l2n_writer_au.txt index 9254f0d88..c61190f22 100644 --- a/testdata/algo/l2n_writer_au.txt +++ b/testdata/algo/l2n_writer_au.txt @@ -86,7 +86,7 @@ device(D$NMOS$1 NMOS circuit(INV2 # Nets with their geometries - net(IN 1 + net(1 name(IN) rect(poly -525 -250 -275 2250) rect(poly -1700 1620 -400 1980) rect(poly -525 -800 -275 800) @@ -94,7 +94,7 @@ circuit(INV2 rect(poly_lbl -801 1799 -799 1801) rect(poly_cont -1630 1690 -1410 1910) ) - net($2 2 + net(2 rect(poly 275 -250 525 2250) rect(poly 220 820 580 1180) rect(poly 275 2000 525 3600) @@ -111,7 +111,7 @@ circuit(INV2 rect(psd -1050 2325 -525 3275) rect(nsd -1050 -475 -525 475) ) - net(OUT 3 + net(3 name(OUT) rect(diff_cont 690 2890 910 3110) rect(diff_cont 690 2490 910 2710) rect(diff_cont 690 90 910 310) @@ -123,7 +123,7 @@ circuit(INV2 rect(psd 525 2325 1050 3275) rect(nsd 525 -475 1050 475) ) - net($4 4 + net(4 rect(diff_cont -110 -310 110 -90) rect(diff_cont -110 90 110 310) rect(diff_cont -110 90 110 310) @@ -135,7 +135,7 @@ circuit(INV2 rect(metal2 -1400 -450 1400 450) rect(nsd -275 -475 275 475) ) - net($5 5 + net(5 rect(diff_cont -110 2490 110 2710) rect(diff_cont -110 2890 110 3110) rect(diff_cont -110 2890 110 3110) @@ -209,7 +209,7 @@ circuit(INV2 circuit(RINGO # Nets with their geometries - net(FB 1 + net(1 name(FB) rect(diff_cont 22850 2490 23070 2710) rect(diff_cont 22850 2890 23070 3110) rect(diff_cont 22850 -310 23070 -90) @@ -220,7 +220,7 @@ circuit(RINGO rect(metal2 -1720 1600 23160 2000) rect(metal2_lbl -1 1799 1 1801) ) - net(OSC 2 + net(2 name(OSC) rect(diff_cont 24450 2890 24670 3110) rect(diff_cont 24450 2490 24670 2710) rect(diff_cont 24450 90 24670 310) @@ -229,7 +229,7 @@ circuit(RINGO rect(metal2 24360 1600 24760 2000) rect(metal2_lbl 24559 1799 24561 1801) ) - net(VSS 3 + net(3 name(VSS) rect(diff_cont 2530 -310 2750 -90) rect(diff_cont 2530 90 2750 310) rect(diff_cont 2530 90 2750 310) @@ -292,7 +292,7 @@ circuit(RINGO rect(metal1 23580 -380 23940 380) rect(metal2_lbl -1 -1 1 1) ) - net(VDD 4 + net(4 name(VDD) rect(diff_cont 2530 2490 2750 2710) rect(diff_cont 2530 2890 2750 3110) rect(diff_cont 2530 2890 2750 3110) @@ -355,55 +355,55 @@ circuit(RINGO rect(metal1 23580 2420 23940 3180) rect(metal2_lbl -1 2799 1 2801) ) - net($I19 5 + net(5 rect(diff_cont 690 2890 910 3110) rect(diff_cont 690 2490 910 2710) rect(diff_cont 690 90 910 310) rect(diff_cont 690 -310 910 -90) ) - net($I8 6 + net(6 rect(diff_cont 21810 2890 22030 3110) rect(diff_cont 21810 2490 22030 2710) rect(diff_cont 21810 90 22030 310) rect(diff_cont 21810 -310 22030 -90) ) - net($I7 7 + net(7 rect(diff_cont 19170 2890 19390 3110) rect(diff_cont 19170 2490 19390 2710) rect(diff_cont 19170 90 19390 310) rect(diff_cont 19170 -310 19390 -90) ) - net($I6 8 + net(8 rect(diff_cont 16530 2890 16750 3110) rect(diff_cont 16530 2490 16750 2710) rect(diff_cont 16530 90 16750 310) rect(diff_cont 16530 -310 16750 -90) ) - net($I5 9 + net(9 rect(diff_cont 13890 2890 14110 3110) rect(diff_cont 13890 2490 14110 2710) rect(diff_cont 13890 90 14110 310) rect(diff_cont 13890 -310 14110 -90) ) - net($I4 10 + net(10 rect(diff_cont 11250 2890 11470 3110) rect(diff_cont 11250 2490 11470 2710) rect(diff_cont 11250 90 11470 310) rect(diff_cont 11250 -310 11470 -90) ) - net($I3 11 + net(11 rect(diff_cont 8610 2890 8830 3110) rect(diff_cont 8610 2490 8830 2710) rect(diff_cont 8610 90 8830 310) rect(diff_cont 8610 -310 8830 -90) ) - net($I2 12 + net(12 rect(diff_cont 5970 2890 6190 3110) rect(diff_cont 5970 2490 6190 2710) rect(diff_cont 5970 90 6190 310) rect(diff_cont 5970 -310 6190 -90) ) - net($I1 13 + net(13 rect(diff_cont 3330 2890 3550 3110) rect(diff_cont 3330 2490 3550 2710) rect(diff_cont 3330 90 3550 310) diff --git a/testdata/algo/l2n_writer_au_2.txt b/testdata/algo/l2n_writer_au_2.txt index 1dcc6a6a7..8138e22f3 100644 --- a/testdata/algo/l2n_writer_au_2.txt +++ b/testdata/algo/l2n_writer_au_2.txt @@ -109,12 +109,12 @@ device(D$NMOS$1 NMOS circuit(INV2 # Nets with their geometries - net($1 1 + net(1 rect(nwell -1400 1800 1400 4580) rect(diff_cont -110 3930 110 4150) rect(ntie -400 3700 400 4380) ) - net(IN 2 + net(2 name(IN) rect(poly -525 -250 -275 2250) rect(poly -1700 1620 -400 1980) rect(poly -525 -800 -275 800) @@ -122,7 +122,7 @@ circuit(INV2 rect(poly_lbl -801 1799 -799 1801) rect(poly_cont -1630 1690 -1410 1910) ) - net($3 3 + net(3 rect(poly 275 -250 525 2250) rect(poly 220 820 580 1180) rect(poly 275 2000 525 3600) @@ -139,7 +139,7 @@ circuit(INV2 rect(psd -1050 2325 -525 3275) rect(nsd -1050 -475 -525 475) ) - net(OUT 4 + net(4 name(OUT) rect(diff_cont 690 2890 910 3110) rect(diff_cont 690 2490 910 2710) rect(diff_cont 690 90 910 310) @@ -151,7 +151,7 @@ circuit(INV2 rect(psd 525 2325 1050 3275) rect(nsd 525 -475 1050 475) ) - net(VSS 5 + net(5 name(VSS) rect(diff_cont -110 -310 110 -90) rect(diff_cont -110 90 110 310) rect(diff_cont -110 90 110 310) @@ -164,7 +164,7 @@ circuit(INV2 rect(metal2_lbl 1239 -91 1241 -89) rect(nsd -275 -475 275 475) ) - net(VDD 6 + net(6 name(VDD) rect(diff_cont -110 2490 110 2710) rect(diff_cont -110 2890 110 3110) rect(diff_cont -110 2890 110 3110) @@ -177,7 +177,7 @@ circuit(INV2 rect(metal2_lbl 1249 2799 1251 2801) rect(psd -275 2325 275 3275) ) - net(BULK 7 + net(7 name(BULK) rect(diff_cont -110 -1360 110 -1140) rect(ptie -400 -1590 400 -910) ) @@ -249,14 +249,14 @@ circuit(INV2 circuit(INV2PAIR # Nets with their geometries - net(BULK 1) - net($I8 2 + net(1 name(BULK)) + net(2 rect(diff_cont 3430 3290 3650 3510) rect(diff_cont 3430 3690 3650 3910) rect(diff_cont 3430 490 3650 710) rect(diff_cont 3430 890 3650 1110) ) - net($I6 3 + net(3 rect(diff_cont 4230 3290 4450 3510) rect(diff_cont 4230 3690 4450 3910) rect(diff_cont 4230 3690 4450 3910) @@ -270,7 +270,7 @@ circuit(INV2PAIR rect(metal1 1520 3220 1880 3980) rect(metal1 1520 3220 1880 3980) ) - net($I5 4 + net(4 rect(diff_cont 4230 490 4450 710) rect(diff_cont 4230 890 4450 1110) rect(diff_cont 4230 890 4450 1110) @@ -284,20 +284,20 @@ circuit(INV2PAIR rect(metal1 1520 420 1880 1180) rect(metal1 1520 420 1880 1180) ) - net($I4 5 + net(5 rect(diff_cont 2390 3690 2610 3910) rect(diff_cont 2390 3290 2610 3510) rect(diff_cont 2390 890 2610 1110) rect(diff_cont 2390 490 2610 710) ) - net($I3 6) - net($I2 7 + net(6) + net(7 rect(diff_cont 5030 3690 5250 3910) rect(diff_cont 5030 3290 5250 3510) rect(diff_cont 5030 890 5250 1110) rect(diff_cont 5030 490 5250 710) ) - net($I1 8) + net(8) # Outgoing pins and their connections to nets pin(BULK 1) @@ -331,7 +331,7 @@ circuit(INV2PAIR circuit(RINGO # Nets with their geometries - net(FB 1 + net(1 name(FB) rect(diff_cont 22850 2490 23070 2710) rect(diff_cont 22850 2890 23070 3110) rect(diff_cont 22850 -310 23070 -90) @@ -342,7 +342,7 @@ circuit(RINGO rect(metal2 -1720 1600 23160 2000) rect(metal2_lbl -1 1799 1 1801) ) - net(OSC 2 + net(2 name(OSC) rect(diff_cont 24450 2890 24670 3110) rect(diff_cont 24450 2490 24670 2710) rect(diff_cont 24450 90 24670 310) @@ -351,7 +351,7 @@ circuit(RINGO rect(metal2 24360 1600 24760 2000) rect(metal2_lbl 24559 1799 24561 1801) ) - net(VDD 3 + net(3 name(VDD) rect(diff_cont 7810 2490 8030 2710) rect(diff_cont 7810 2890 8030 3110) rect(diff_cont 7810 2890 8030 3110) @@ -424,7 +424,7 @@ circuit(RINGO rect(metal1 20940 2420 21300 3180) rect(metal2_lbl -1 2799 1 2801) ) - net('BULK,VSS' 4 + net(4 name('BULK,VSS') rect(diff_cont 7810 -310 8030 -90) rect(diff_cont 7810 90 8030 310) rect(diff_cont 7810 90 8030 310) @@ -497,25 +497,25 @@ circuit(RINGO rect(metal1 20940 -380 21300 380) rect(metal2_lbl -1 -1 1 1) ) - net($I13 5 + net(5 rect(diff_cont 3330 2890 3550 3110) rect(diff_cont 3330 2490 3550 2710) rect(diff_cont 3330 90 3550 310) rect(diff_cont 3330 -310 3550 -90) ) - net($I7 6 + net(6 rect(diff_cont 19170 2890 19390 3110) rect(diff_cont 19170 2490 19390 2710) rect(diff_cont 19170 90 19390 310) rect(diff_cont 19170 -310 19390 -90) ) - net($I6 7 + net(7 rect(diff_cont 13890 2890 14110 3110) rect(diff_cont 13890 2490 14110 2710) rect(diff_cont 13890 90 14110 310) rect(diff_cont 13890 -310 14110 -90) ) - net($I5 8 + net(8 rect(diff_cont 8610 2890 8830 3110) rect(diff_cont 8610 2490 8830 2710) rect(diff_cont 8610 90 8830 310) diff --git a/testdata/algo/l2n_writer_au_2s.txt b/testdata/algo/l2n_writer_au_2s.txt index 5d7f46129..f89b7692e 100644 --- a/testdata/algo/l2n_writer_au_2s.txt +++ b/testdata/algo/l2n_writer_au_2s.txt @@ -89,12 +89,12 @@ D(D$NMOS$1 NMOS ) ) X(INV2 - N($1 1 + N(1 R(nwell -1400 1800 1400 4580) R(diff_cont -110 3930 110 4150) R(ntie -400 3700 400 4380) ) - N(IN 2 + N(2 I(IN) R(poly -525 -250 -275 2250) R(poly -1700 1620 -400 1980) R(poly -525 -800 -275 800) @@ -102,7 +102,7 @@ X(INV2 R(poly_lbl -801 1799 -799 1801) R(poly_cont -1630 1690 -1410 1910) ) - N($3 3 + N(3 R(poly 275 -250 525 2250) R(poly 220 820 580 1180) R(poly 275 2000 525 3600) @@ -119,7 +119,7 @@ X(INV2 R(psd -1050 2325 -525 3275) R(nsd -1050 -475 -525 475) ) - N(OUT 4 + N(4 I(OUT) R(diff_cont 690 2890 910 3110) R(diff_cont 690 2490 910 2710) R(diff_cont 690 90 910 310) @@ -131,7 +131,7 @@ X(INV2 R(psd 525 2325 1050 3275) R(nsd 525 -475 1050 475) ) - N(VSS 5 + N(5 I(VSS) R(diff_cont -110 -310 110 -90) R(diff_cont -110 90 110 310) R(diff_cont -110 90 110 310) @@ -144,7 +144,7 @@ X(INV2 R(metal2_lbl 1239 -91 1241 -89) R(nsd -275 -475 275 475) ) - N(VDD 6 + N(6 I(VDD) R(diff_cont -110 2490 110 2710) R(diff_cont -110 2890 110 3110) R(diff_cont -110 2890 110 3110) @@ -157,7 +157,7 @@ X(INV2 R(metal2_lbl 1249 2799 1251 2801) R(psd -275 2325 275 3275) ) - N(BULK 7 + N(7 I(BULK) R(diff_cont -110 -1360 110 -1140) R(ptie -400 -1590 400 -910) ) @@ -222,14 +222,14 @@ X(INV2 ) ) X(INV2PAIR - N(BULK 1) - N($I8 2 + N(1 I(BULK)) + N(2 R(diff_cont 3430 3290 3650 3510) R(diff_cont 3430 3690 3650 3910) R(diff_cont 3430 490 3650 710) R(diff_cont 3430 890 3650 1110) ) - N($I6 3 + N(3 R(diff_cont 4230 3290 4450 3510) R(diff_cont 4230 3690 4450 3910) R(diff_cont 4230 3690 4450 3910) @@ -243,7 +243,7 @@ X(INV2PAIR R(metal1 1520 3220 1880 3980) R(metal1 1520 3220 1880 3980) ) - N($I5 4 + N(4 R(diff_cont 4230 490 4450 710) R(diff_cont 4230 890 4450 1110) R(diff_cont 4230 890 4450 1110) @@ -257,20 +257,20 @@ X(INV2PAIR R(metal1 1520 420 1880 1180) R(metal1 1520 420 1880 1180) ) - N($I4 5 + N(5 R(diff_cont 2390 3690 2610 3910) R(diff_cont 2390 3290 2610 3510) R(diff_cont 2390 890 2610 1110) R(diff_cont 2390 490 2610 710) ) - N($I3 6) - N($I2 7 + N(6) + N(7 R(diff_cont 5030 3690 5250 3910) R(diff_cont 5030 3290 5250 3510) R(diff_cont 5030 890 5250 1110) R(diff_cont 5030 490 5250 710) ) - N($I1 8) + N(8) P(BULK 1) P($1 2) P($2 3) @@ -297,7 +297,7 @@ X(INV2PAIR ) ) X(RINGO - N(FB 1 + N(1 I(FB) R(diff_cont 22850 2490 23070 2710) R(diff_cont 22850 2890 23070 3110) R(diff_cont 22850 -310 23070 -90) @@ -308,7 +308,7 @@ X(RINGO R(metal2 -1720 1600 23160 2000) R(metal2_lbl -1 1799 1 1801) ) - N(OSC 2 + N(2 I(OSC) R(diff_cont 24450 2890 24670 3110) R(diff_cont 24450 2490 24670 2710) R(diff_cont 24450 90 24670 310) @@ -317,7 +317,7 @@ X(RINGO R(metal2 24360 1600 24760 2000) R(metal2_lbl 24559 1799 24561 1801) ) - N(VDD 3 + N(3 I(VDD) R(diff_cont 7810 2490 8030 2710) R(diff_cont 7810 2890 8030 3110) R(diff_cont 7810 2890 8030 3110) @@ -390,7 +390,7 @@ X(RINGO R(metal1 20940 2420 21300 3180) R(metal2_lbl -1 2799 1 2801) ) - N('BULK,VSS' 4 + N(4 I('BULK,VSS') R(diff_cont 7810 -310 8030 -90) R(diff_cont 7810 90 8030 310) R(diff_cont 7810 90 8030 310) @@ -463,25 +463,25 @@ X(RINGO R(metal1 20940 -380 21300 380) R(metal2_lbl -1 -1 1 1) ) - N($I13 5 + N(5 R(diff_cont 3330 2890 3550 3110) R(diff_cont 3330 2490 3550 2710) R(diff_cont 3330 90 3550 310) R(diff_cont 3330 -310 3550 -90) ) - N($I7 6 + N(6 R(diff_cont 19170 2890 19390 3110) R(diff_cont 19170 2490 19390 2710) R(diff_cont 19170 90 19390 310) R(diff_cont 19170 -310 19390 -90) ) - N($I6 7 + N(7 R(diff_cont 13890 2890 14110 3110) R(diff_cont 13890 2490 14110 2710) R(diff_cont 13890 90 14110 310) R(diff_cont 13890 -310 14110 -90) ) - N($I5 8 + N(8 R(diff_cont 8610 2890 8830 3110) R(diff_cont 8610 2490 8830 2710) R(diff_cont 8610 90 8830 310) diff --git a/testdata/algo/l2n_writer_au_s.txt b/testdata/algo/l2n_writer_au_s.txt index 6be18f125..e60473552 100644 --- a/testdata/algo/l2n_writer_au_s.txt +++ b/testdata/algo/l2n_writer_au_s.txt @@ -68,7 +68,7 @@ D(D$NMOS$1 NMOS ) ) X(INV2 - N(IN 1 + N(1 I(IN) R(poly -525 -250 -275 2250) R(poly -1700 1620 -400 1980) R(poly -525 -800 -275 800) @@ -76,7 +76,7 @@ X(INV2 R(poly_lbl -801 1799 -799 1801) R(poly_cont -1630 1690 -1410 1910) ) - N($2 2 + N(2 R(poly 275 -250 525 2250) R(poly 220 820 580 1180) R(poly 275 2000 525 3600) @@ -93,7 +93,7 @@ X(INV2 R(psd -1050 2325 -525 3275) R(nsd -1050 -475 -525 475) ) - N(OUT 3 + N(3 I(OUT) R(diff_cont 690 2890 910 3110) R(diff_cont 690 2490 910 2710) R(diff_cont 690 90 910 310) @@ -105,7 +105,7 @@ X(INV2 R(psd 525 2325 1050 3275) R(nsd 525 -475 1050 475) ) - N($4 4 + N(4 R(diff_cont -110 -310 110 -90) R(diff_cont -110 90 110 310) R(diff_cont -110 90 110 310) @@ -117,7 +117,7 @@ X(INV2 R(metal2 -1400 -450 1400 450) R(nsd -275 -475 275 475) ) - N($5 5 + N(5 R(diff_cont -110 2490 110 2710) R(diff_cont -110 2890 110 3110) R(diff_cont -110 2890 110 3110) @@ -184,7 +184,7 @@ X(INV2 ) ) X(RINGO - N(FB 1 + N(1 I(FB) R(diff_cont 22850 2490 23070 2710) R(diff_cont 22850 2890 23070 3110) R(diff_cont 22850 -310 23070 -90) @@ -195,7 +195,7 @@ X(RINGO R(metal2 -1720 1600 23160 2000) R(metal2_lbl -1 1799 1 1801) ) - N(OSC 2 + N(2 I(OSC) R(diff_cont 24450 2890 24670 3110) R(diff_cont 24450 2490 24670 2710) R(diff_cont 24450 90 24670 310) @@ -204,7 +204,7 @@ X(RINGO R(metal2 24360 1600 24760 2000) R(metal2_lbl 24559 1799 24561 1801) ) - N(VSS 3 + N(3 I(VSS) R(diff_cont 2530 -310 2750 -90) R(diff_cont 2530 90 2750 310) R(diff_cont 2530 90 2750 310) @@ -267,7 +267,7 @@ X(RINGO R(metal1 23580 -380 23940 380) R(metal2_lbl -1 -1 1 1) ) - N(VDD 4 + N(4 I(VDD) R(diff_cont 2530 2490 2750 2710) R(diff_cont 2530 2890 2750 3110) R(diff_cont 2530 2890 2750 3110) @@ -330,55 +330,55 @@ X(RINGO R(metal1 23580 2420 23940 3180) R(metal2_lbl -1 2799 1 2801) ) - N($I19 5 + N(5 R(diff_cont 690 2890 910 3110) R(diff_cont 690 2490 910 2710) R(diff_cont 690 90 910 310) R(diff_cont 690 -310 910 -90) ) - N($I8 6 + N(6 R(diff_cont 21810 2890 22030 3110) R(diff_cont 21810 2490 22030 2710) R(diff_cont 21810 90 22030 310) R(diff_cont 21810 -310 22030 -90) ) - N($I7 7 + N(7 R(diff_cont 19170 2890 19390 3110) R(diff_cont 19170 2490 19390 2710) R(diff_cont 19170 90 19390 310) R(diff_cont 19170 -310 19390 -90) ) - N($I6 8 + N(8 R(diff_cont 16530 2890 16750 3110) R(diff_cont 16530 2490 16750 2710) R(diff_cont 16530 90 16750 310) R(diff_cont 16530 -310 16750 -90) ) - N($I5 9 + N(9 R(diff_cont 13890 2890 14110 3110) R(diff_cont 13890 2490 14110 2710) R(diff_cont 13890 90 14110 310) R(diff_cont 13890 -310 14110 -90) ) - N($I4 10 + N(10 R(diff_cont 11250 2890 11470 3110) R(diff_cont 11250 2490 11470 2710) R(diff_cont 11250 90 11470 310) R(diff_cont 11250 -310 11470 -90) ) - N($I3 11 + N(11 R(diff_cont 8610 2890 8830 3110) R(diff_cont 8610 2490 8830 2710) R(diff_cont 8610 90 8830 310) R(diff_cont 8610 -310 8830 -90) ) - N($I2 12 + N(12 R(diff_cont 5970 2890 6190 3110) R(diff_cont 5970 2490 6190 2710) R(diff_cont 5970 90 6190 310) R(diff_cont 5970 -310 6190 -90) ) - N($I1 13 + N(13 R(diff_cont 3330 2890 3550 3110) R(diff_cont 3330 2490 3550 2710) R(diff_cont 3330 90 3550 310) From 9c0123df20a35479c0279ea8251cfd1f52a40396 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 3 Feb 2019 21:34:23 +0100 Subject: [PATCH 237/335] Implemented implicit joining of nets with the same label. --- src/db/db/dbHierNetworkProcessor.cc | 65 ++++- src/db/db/dbHierNetworkProcessor.h | 16 +- src/db/db/dbLayoutToNetlist.cc | 4 +- src/db/db/dbLayoutToNetlist.h | 2 +- src/db/db/dbNetlistExtractor.cc | 32 ++- src/db/db/dbNetlistExtractor.h | 2 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 4 +- src/db/unit_tests/dbNetlistExtractorTests.cc | 244 +++++++++++++++- src/tl/tl/tl.pro | 6 +- src/tl/tl/tlEquivalenceClusters.cc | 30 ++ src/tl/tl/tlEquivalenceClusters.h | 263 ++++++++++++++++++ .../unit_tests/tlEquivalenceClustersTests.cc | 211 ++++++++++++++ src/tl/unit_tests/unit_tests.pro | 3 +- .../algo/device_extract_au1_implicit_nets.gds | Bin 0 -> 9160 bytes .../algo/device_extract_l1_implicit_nets.gds | Bin 0 -> 3276 bytes 15 files changed, 860 insertions(+), 22 deletions(-) create mode 100644 src/tl/tl/tlEquivalenceClusters.cc create mode 100644 src/tl/tl/tlEquivalenceClusters.h create mode 100644 src/tl/unit_tests/tlEquivalenceClustersTests.cc create mode 100644 testdata/algo/device_extract_au1_implicit_nets.gds create mode 100644 testdata/algo/device_extract_l1_implicit_nets.gds diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 57272b113..452d160fc 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -817,7 +817,7 @@ private: template void -local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) +local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence) { bool report_progress = tl::verbosity () >= 50; static std::string desc = tl::to_string (tr ("Building local clusters")); @@ -836,6 +836,57 @@ local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flag cluster_building_receiver rec (conn); bs.process (rec, 1 /*==touching*/, bc); rec.generate_clusters (*this); + + if (attr_equivalence && attr_equivalence->size () > 0) { + apply_attr_equivalences (*attr_equivalence); + } +} + +template +void +local_clusters::apply_attr_equivalences (const tl::equivalence_clusters &attr_equivalence) +{ + tl::equivalence_clusters eq; + + // collect all local attributes (the ones which are present in attr_equivalence) into "eq" + // and form equivalences for multi-attribute clusters. + for (const_iterator c = begin (); c != end (); ++c) { + typename local_cluster::attr_iterator a0; + for (typename local_cluster::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) { + if (attr_equivalence.has_attribute (*a)) { + if (a0 == typename local_cluster::attr_iterator ()) { + a0 = a; + } + eq.same (*a0, *a); + } + } + } + + // apply the equivalences implied by attr_equivalence + eq.apply_equivalences (attr_equivalence); + + // identify the layout clusters joined into one attribute cluster and join them + + std::map::cluster_id_type, std::set > c2c; + + for (const_iterator c = begin (); c != end (); ++c) { + for (typename local_cluster::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) { + tl::equivalence_clusters::cluster_id_type cl = attr_equivalence.cluster_id (*a); + if (cl > 0) { + c2c [cl].insert (c->id ()); + } + } + } + + for (std::map::cluster_id_type, std::set >::const_iterator c = c2c.begin (); c != c2c.end (); ++c) { + if (c->second.size () > 1) { + std::set::const_iterator cl0 = c->second.begin (); + std::set::const_iterator cl = cl0; + while (++cl != c->second.end ()) { + join_cluster_with (*cl0, *cl); + } + } + } } // explicit instantiations @@ -992,11 +1043,11 @@ void hier_clusters::clear () template void -hier_clusters::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) +hier_clusters::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence) { clear (); cell_clusters_box_converter cbc (layout, *this); - do_build (cbc, layout, cell, shape_flags, conn); + do_build (cbc, layout, cell, shape_flags, conn, attr_equivalence); } namespace @@ -1649,7 +1700,7 @@ hier_clusters::make_path (const db::Layout &layout, const db::Cell &cell, siz template void -hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) +hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing shape clusters"))); @@ -1664,7 +1715,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou tl::RelativeProgress progress (tl::to_string (tr ("Computing local clusters")), called.size (), 1); for (std::set::const_iterator c = called.begin (); c != called.end (); ++c) { - build_local_cluster (layout, layout.cell (*c), shape_flags, conn); + build_local_cluster (layout, layout.cell (*c), shape_flags, conn, attr_equivalence); ++progress; } } @@ -1707,7 +1758,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou template void -hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) +hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence) { std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); if (tl::verbosity () >= 40) { @@ -1716,7 +1767,7 @@ hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell tl::SelfTimer timer (tl::verbosity () >= 41, msg); connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; - local.build_clusters (cell, shape_flags, conn); + local.build_clusters (cell, shape_flags, conn, attr_equivalence); } template diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 46dc00f54..aed77cd28 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -30,6 +30,7 @@ #include "dbBoxTree.h" #include "dbCell.h" #include "dbInstElement.h" +#include "tlEquivalenceClusters.h" #include #include @@ -463,8 +464,13 @@ public: * This method will only build the local clusters. Child cells * are not taken into account. Only the shape types listed in * shape_flags are taken. + * + * If attr_equivalence is non-null, all clusters with attributes + * listed as equivalent in this object are joined. Additional + * cluster joining may happen in this case, because multi-attribute + * assignment might create connections too. */ - void build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); + void build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence = 0); /** * @brief Creates and inserts a new clusters @@ -490,6 +496,8 @@ private: box_type m_bbox; tree_type m_clusters; size_t m_next_dummy_id; + + void apply_attr_equivalences (const tl::equivalence_clusters &attr_equivalence); }; /** @@ -740,7 +748,7 @@ public: /** * @brief Builds a hierarchy of clusters from a cell hierarchy and given connectivity */ - void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); + void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence = 0); /** * @brief Gets the connected clusters for a given cell @@ -778,10 +786,10 @@ public: ClusterInstance make_path (const db::Layout &layout, const db::Cell &cell, size_t id, const std::vector &path); private: - void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); + void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence); void build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn); void build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn, tl::RelativeProgress &progress); - void do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn); + void do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence = 0); std::map > m_per_cell_clusters; }; diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index d0955b5ad..556cb93dc 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -226,7 +226,7 @@ size_t LayoutToNetlist::global_net_id (const std::string &name) return m_conn.global_net_id (name); } -void LayoutToNetlist::extract_netlist () +void LayoutToNetlist::extract_netlist (bool join_nets_by_label) { if (m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); @@ -236,7 +236,7 @@ void LayoutToNetlist::extract_netlist () } db::NetlistExtractor netex; - netex.extract_nets(m_dss, m_conn, *mp_netlist, m_net_clusters); + netex.extract_nets(m_dss, m_conn, *mp_netlist, m_net_clusters, join_nets_by_label); m_netlist_extracted = true; } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 4379fb202..30a46063d 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -269,7 +269,7 @@ public: * @brief Runs the netlist extraction * See the class description for more details. */ - void extract_netlist (); + void extract_netlist (bool join_nets_by_label = true); /** * @brief Marks the netlist as extracted diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index b1227f916..fc3d4f836 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -33,8 +33,32 @@ NetlistExtractor::NetlistExtractor () // .. nothing yet .. } +static void +build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, tl::equivalence_clusters &eq) +{ + std::map > prop_by_name; + + for (db::PropertiesRepository::iterator i = layout->properties_repository ().begin (); i != layout->properties_repository ().end (); ++i) { + for (db::PropertiesRepository::properties_set::const_iterator p = i->second.begin (); p != i->second.end (); ++p) { + if (p->first == net_name_id) { + std::string nn = p->second.to_string (); + prop_by_name [nn].insert (i->first); + } + } + } + + for (std::map >::const_iterator pn = prop_by_name.begin (); pn != prop_by_name.end (); ++pn) { + std::set::const_iterator p = pn->second.begin (); + std::set::const_iterator p0 = p; + while (p != pn->second.end ()) { + eq.same (*p0, *p); + ++p; + } + } +} + void -NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters) +NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label) { mp_clusters = &clusters; mp_layout = &dss.const_layout (); @@ -52,7 +76,11 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect // the big part: actually extract the nets - mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn); + tl::equivalence_clusters net_name_equivalence; + if (m_text_annot_name_id.first && join_nets_by_label) { + build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, net_name_equivalence); + } + mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn, &net_name_equivalence); // reverse lookup for Circuit vs. cell index std::map circuits; diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index 3189eefec..c76c6c75f 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -82,7 +82,7 @@ public: * @brief Extract the nets * See the class description for more details. */ - void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters); + void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label = true); private: hier_clusters_type *mp_clusters; diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 7fc2e642b..153b6601b 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -213,8 +213,10 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"), "@brief Gets the global net name for the given global net ID." ) + - gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, + gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_nets_by_label", true), "@brief Runs the netlist extraction\n" + "If join_nets_by_label is true, nets on the same hierarchy level carrying the same label will be connected " + "implicitly even if there is no physical connection.\n" "See the class description for more details.\n" ) + gsi::method_ext ("internal_layout", &l2n_internal_layout, diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index aaaa44a27..3b03ea926 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -465,7 +465,8 @@ TEST(2_DeviceAndNetExtractionFlat) // extract the nets - net_ex.extract_nets (dss, conn, nl, cl); + // don't use "join_nets_by_label" because the flattened texts will spoil everything + net_ex.extract_nets (dss, conn, nl, cl, false); // debug layers produced for nets // 202/0 -> Active @@ -547,3 +548,244 @@ TEST(2_DeviceAndNetExtractionFlat) db::compare_layouts (_this, ly, au); } + +static bool +all_net_names_unique (const db::Circuit &c) +{ + std::set names; + for (db::Circuit::const_net_iterator n = c.begin_nets (); n != c.end_nets (); ++n) { + if (! n->name ().empty ()) { + if (names.find (n->name ()) != names.end ()) { + return false; + } else { + names.insert (n->name ()); + } + } + } + return true; +} + +static bool +all_net_names_unique (const db::Netlist &nl) +{ + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + if (! all_net_names_unique (*c)) { + return false; + } + } + return true; +} + +TEST(3_DeviceAndNetExtractionWithImplicitConnections) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1_implicit_nets.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("LABEL")); + + // original layers + db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); + db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss); + db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss); + db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss); + db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss); + db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss); + db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss); + db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss); + db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss); + db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss); + + // derived regions + + db::Region rpactive = ractive & rnwell; + db::Region rpgate = rpactive & rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = ractive - rnwell; + db::Region rngate = rnactive & rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + + // perform the extraction + + db::Netlist nl; + db::hier_clusters cl; + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + pmos_ex.extract (dss, dl, nl, cl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + nmos_ex.extract (dss, dl, nl, cl); + + // perform the net extraction + + db::NetlistExtractor net_ex; + + db::Connectivity conn; + // Intra-layer + conn.connect (rpsd); + conn.connect (rnsd); + conn.connect (rpoly); + conn.connect (rdiff_cont); + conn.connect (rpoly_cont); + conn.connect (rmetal1); + conn.connect (rvia1); + conn.connect (rmetal2); + // Inter-layer + conn.connect (rpsd, rdiff_cont); + conn.connect (rnsd, rdiff_cont); + conn.connect (rpoly, rpoly_cont); + conn.connect (rpoly_cont, rmetal1); + conn.connect (rdiff_cont, rmetal1); + conn.connect (rmetal1, rvia1); + conn.connect (rvia1, rmetal2); + conn.connect (rpoly, rpoly_lbl); // attaches labels + conn.connect (rmetal1, rmetal1_lbl); // attaches labels + conn.connect (rmetal2, rmetal2_lbl); // attaches labels + + // extract the nets + + net_ex.extract_nets (dss, conn, nl, cl); + + EXPECT_EQ (all_net_names_unique (nl), true); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + std::map dump_map; + dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); + dump_nets_to_layout (nl, cl, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (nl.to_string (), + "Circuit RINGO ():\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $3 (IN=NEXT,$2=$I43,OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $4 (IN=$I3,$2=$I42,OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $5 (IN=$I5,$2=$I44,OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $6 (IN=$I6,$2=$I45,OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $7 (IN=$I7,$2=$I46,OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $8 (IN=$I19,$2=$I39,OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $9 (IN=$I1,$2=$I40,OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $10 (IN=$I2,$2=$I41,OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" + " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" + " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" + " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // doesn't do anything here, but we test that this does not destroy anything: + nl.combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + nl.make_top_level_pins (); + nl.purge (); + + // compare netlist as string + EXPECT_EQ (nl.to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,NEXT=NEXT,'VSSZ,VSS'='VSSZ,VSS','VDDZ,VDD'='VDDZ,VDD'):\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $3 (IN=NEXT,$2=(null),OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $4 (IN=$I3,$2=(null),OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $5 (IN=$I5,$2=(null),OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $6 (IN=$I6,$2=(null),OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $7 (IN=$I7,$2=(null),OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $8 (IN=$I19,$2=(null),OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $9 (IN=$I1,$2=(null),OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $10 (IN=$I2,$2=(null),OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_implicit_nets.gds"); + + db::compare_layouts (_this, ly, au); +} + diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index 850bced12..95deceb8c 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -43,7 +43,8 @@ SOURCES = \ tlUri.cc \ tlLongInt.cc \ tlUniqueId.cc \ - tlList.cc + tlList.cc \ + tlEquivalenceClusters.cc HEADERS = \ tlAlgorithm.h \ @@ -96,7 +97,8 @@ HEADERS = \ tlUri.h \ tlLongInt.h \ tlUniqueId.h \ - tlList.h + tlList.h \ + tlEquivalenceClusters.h equals(HAVE_CURL, "1") { diff --git a/src/tl/tl/tlEquivalenceClusters.cc b/src/tl/tl/tlEquivalenceClusters.cc new file mode 100644 index 000000000..519d6aa40 --- /dev/null +++ b/src/tl/tl/tlEquivalenceClusters.cc @@ -0,0 +1,30 @@ + +/* + + 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 "tlEquivalenceClusters.h" + +namespace tl +{ + + // .. nothing yet .. + +} diff --git a/src/tl/tl/tlEquivalenceClusters.h b/src/tl/tl/tlEquivalenceClusters.h new file mode 100644 index 000000000..5d4b23d4c --- /dev/null +++ b/src/tl/tl/tlEquivalenceClusters.h @@ -0,0 +1,263 @@ + +/* + + 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_tlEquivalenceClusters +#define HDR_tlEquivalenceClusters + +#include "tlCommon.h" +#include "tlAssert.h" + +#include +#include +#include +#include + +namespace tl +{ + +/** + * @brief A utility class forming clusters based on equivalence of a certain attribute + * + * To use this class, feed it with equivalences using "is_same", e.g. + * + * @code + * equivalence_clusters eq; + * // forms two clusters: 1,2,5 and 3,4 + * eq.same (1, 2); + * eq.same (3, 4); + * eq.same (1, 5); + * @endcode + * + * Self-equivalence is a way of introduction an attribute without + * an equivalence: + * + * @code + * equivalence_clusters eq; + * // after this, 1 is a known attribute + * eq.same (1, 1); + * eq.has_attribute (1); // ->true + * @endcode + * + * Equivalence clusters can be merged, forming new and bigger clusters. + * + * Eventually, each cluster is represented by a non-zero integer ID. + * The cluster ID can obtained per attribute value using "cluster_id". + * In the above example this will be: + * + * @code + * eq.cluster_id (1); // ->1 + * eq.cluster_id (2); // ->1 + * eq.cluster_id (3); // ->2 + * eq.cluster_id (4); // ->2 + * eq.cluster_id (5); // ->1 + * eq.cluster_id (6); // ->0 (unknown) + * @endcode + * + * "size" will give the maximum cluster ID. + */ +template +class equivalence_clusters +{ +public: + typedef size_t cluster_id_type; + typedef T attribute_type; + typedef typename std::vector::iterator>::const_iterator cluster_iterator; + + /** + * @brief Creates an empty equivalence cluster + */ + equivalence_clusters () + { + // .. nothing yet .. + } + + /** + * @brief Makes attr1 and attr2 equivalent + */ + void same (const T &attr1, const T &attr2) + { + cluster_id_type cl1 = cluster_id (attr1); + + if (attr1 == attr2) { + // special case of "self-identity" + if (! cl1) { + cluster_id_type cl = new_cluster (); + insert (attr1, cl); + } + return; + } + + cluster_id_type cl2 = cluster_id (attr2); + if (! cl1 || ! cl2) { + + if (cl1) { + insert (attr2, cl1); + } else if (cl2) { + insert (attr1, cl2); + } else { + cluster_id_type cl = new_cluster (); + insert (attr1, cl); + insert (attr2, cl); + } + + } else if (cl1 != cl2) { + join (cl1, cl2); + } + } + + /** + * @brief Returns true, if attr is part of the equivalence clusters + */ + bool has_attribute (const T &attr) const + { + return m_cluster_id_by_attr.find (attr) != m_cluster_id_by_attr.end (); + } + + /** + * @brief Returns the cluster ID for the given attribute of 0 if the attribute is not assigned to a cluster + */ + cluster_id_type cluster_id (const T &attr) const + { + typename std::map::const_iterator c = m_cluster_id_by_attr.find (attr); + if (c != m_cluster_id_by_attr.end ()) { + return c->second; + } else { + return 0; + } + } + + /** + * @brief Applies the equivalences of the other clusters + * + * This method will join clusters already within this equivalence cluster collection + * based on the equivalences listed in the other clusters collection. + * + * In contrast to merge, his method will not introduce new attributes. + */ + void apply_equivalences (const equivalence_clusters &other) + { + std::vector attrs; + for (typename std::map::const_iterator a = m_cluster_id_by_attr.begin (); a != m_cluster_id_by_attr.end (); ++a) { + if (other.has_attribute (a->first)) { + attrs.push_back (a->first); + } + } + + for (typename std::vector::const_iterator a = attrs.begin (); a != attrs.end (); ++a) { + cluster_id_type cl = other.cluster_id (*a); + cluster_iterator b = other.begin_cluster (cl); + cluster_iterator e = other.end_cluster (cl); + for (cluster_iterator i = b; i != e; ++i) { + if ((*i)->first != *a && has_attribute ((*i)->first)) { + same ((*i)->first, *a); + } + } + } + } + + /** + * @brief Merges the equivalences of the other clusters into this + * + * This method will add all attributes from the other cluster collection and join + * clusters based on the equivalences there. + */ + void merge (const equivalence_clusters &other) + { + for (cluster_id_type cl = 1; cl <= other.size (); ++cl) { + cluster_iterator b = other.begin_cluster (cl); + cluster_iterator e = other.end_cluster (cl); + for (cluster_iterator i = b; i != e; ++i) { + same ((*b)->first, (*i)->first); + } + } + } + + /** + * @brief Returns the number of clusters kept inside this collection + */ + size_t size () const + { + return m_clusters.size (); + } + + /** + * @brief Begin iterator for the cluster elements for a given cluster ID. + * To access, the attribute, use "(*i)->first". + */ + cluster_iterator begin_cluster (size_t cluster_id) const + { + tl_assert (cluster_id > 0); + return m_clusters [cluster_id - 1].begin (); + } + + /** + * @brief End iterator for the cluster elements for a given cluster ID. + */ + cluster_iterator end_cluster (size_t cluster_id) const + { + tl_assert (cluster_id > 0); + return m_clusters [cluster_id - 1].end (); + } + +private: + void insert (const T &attr, cluster_id_type into) + { + typename std::map::iterator c = m_cluster_id_by_attr.insert (std::make_pair (attr, into)).first; + m_clusters [into - 1].push_back (c); + } + + void join (cluster_id_type id, cluster_id_type with_id) + { + tl_assert (id > 0); + tl_assert (with_id > 0); + std::vector::iterator> &cnew = m_clusters [id - 1]; + std::vector::iterator> &c = m_clusters [with_id - 1]; + for (typename std::vector::iterator>::iterator i = c.begin (); i != c.end (); ++i) { + (*i)->second = id; + cnew.push_back (*i); + } + + c.clear (); + m_free_slots.push_back (with_id); + } + + cluster_id_type new_cluster () + { + if (! m_free_slots.empty ()) { + cluster_id_type cl = m_free_slots.back (); + m_free_slots.pop_back (); + return cl; + } else { + m_clusters.push_back (std::vector::iterator> ()); + return m_clusters.size (); + } + } + + std::map m_cluster_id_by_attr; + std::vector::iterator> > m_clusters; + std::vector m_free_slots; +}; + +} + +#endif diff --git a/src/tl/unit_tests/tlEquivalenceClustersTests.cc b/src/tl/unit_tests/tlEquivalenceClustersTests.cc new file mode 100644 index 000000000..5d48edd5c --- /dev/null +++ b/src/tl/unit_tests/tlEquivalenceClustersTests.cc @@ -0,0 +1,211 @@ + +/* + + 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 "tlEquivalenceClusters.h" +#include "tlUnitTest.h" + +// basics +TEST(1_basics) +{ + tl::equivalence_clusters eq; + + eq.same (1, 5); + eq.same (2, 3); + eq.same (5, 4); + + EXPECT_EQ (eq.cluster_id (1), size_t (1)); + EXPECT_EQ (eq.cluster_id (2), size_t (2)); + EXPECT_EQ (eq.cluster_id (3), size_t (2)); + EXPECT_EQ (eq.cluster_id (4), size_t (1)); + EXPECT_EQ (eq.cluster_id (5), size_t (1)); + EXPECT_EQ (eq.cluster_id (6), size_t (0)); + + EXPECT_EQ (eq.size (), size_t (2)); + + eq.same (2, 6); + EXPECT_EQ (eq.cluster_id (6), size_t (2)); + EXPECT_EQ (eq.cluster_id (7), size_t (0)); + + EXPECT_EQ (eq.size (), size_t (2)); + + eq.same (7, 8); + EXPECT_EQ (eq.size (), size_t (3)); + EXPECT_EQ (eq.cluster_id (6), size_t (2)); + EXPECT_EQ (eq.cluster_id (7), size_t (3)); + EXPECT_EQ (eq.cluster_id (8), size_t (3)); +} + +// joining of clusters +TEST(2_join) +{ + tl::equivalence_clusters eq; + + eq.same (1, 2); + eq.same (3, 4); + eq.same (5, 6); + + EXPECT_EQ (eq.cluster_id (1), size_t (1)); + EXPECT_EQ (eq.cluster_id (2), size_t (1)); + EXPECT_EQ (eq.cluster_id (3), size_t (2)); + EXPECT_EQ (eq.cluster_id (4), size_t (2)); + EXPECT_EQ (eq.cluster_id (5), size_t (3)); + EXPECT_EQ (eq.cluster_id (6), size_t (3)); + + eq.same (3, 2); + + EXPECT_EQ (eq.cluster_id (1), size_t (2)); + EXPECT_EQ (eq.cluster_id (2), size_t (2)); + EXPECT_EQ (eq.cluster_id (3), size_t (2)); + EXPECT_EQ (eq.cluster_id (4), size_t (2)); + EXPECT_EQ (eq.cluster_id (5), size_t (3)); + EXPECT_EQ (eq.cluster_id (6), size_t (3)); + + eq.same (4, 5); + + EXPECT_EQ (eq.cluster_id (1), size_t (2)); + EXPECT_EQ (eq.cluster_id (2), size_t (2)); + EXPECT_EQ (eq.cluster_id (3), size_t (2)); + EXPECT_EQ (eq.cluster_id (4), size_t (2)); + EXPECT_EQ (eq.cluster_id (5), size_t (2)); + EXPECT_EQ (eq.cluster_id (6), size_t (2)); + + eq.same (10, 11); + eq.same (12, 13); + + EXPECT_EQ (eq.cluster_id (10), size_t (3)); + EXPECT_EQ (eq.cluster_id (11), size_t (3)); + + EXPECT_EQ (eq.cluster_id (12), size_t (1)); + EXPECT_EQ (eq.cluster_id (13), size_t (1)); +} + +// size +TEST(3_size) +{ + tl::equivalence_clusters eq; + + eq.same (1, 2); + EXPECT_EQ (eq.size (), size_t (1)); + eq.same (2, 4); + EXPECT_EQ (eq.size (), size_t (1)); + eq.same (5, 6); + EXPECT_EQ (eq.size (), size_t (2)); +} + +// has_attribute +TEST(4_has_attribute) +{ + tl::equivalence_clusters eq; + + eq.same (1, 1); + EXPECT_EQ (eq.has_attribute (1), true); + EXPECT_EQ (eq.has_attribute (2), false); + eq.same (1, 2); + EXPECT_EQ (eq.has_attribute (1), true); + EXPECT_EQ (eq.has_attribute (2), true); + EXPECT_EQ (eq.has_attribute (3), false); +} + +std::string eq2string (const tl::equivalence_clusters &eq) +{ + std::string res; + for (size_t c = 1; c <= eq.size (); ++c) { + for (tl::equivalence_clusters::cluster_iterator i = eq.begin_cluster (c); i != eq.end_cluster (c); ++i) { + if (i != eq.begin_cluster (c)) { + res += ","; + } else if (! res.empty ()) { + res += ";"; + } + res += tl::to_string ((*i)->first); + } + } + return res; +} + +// iterator +TEST(5_iterator) +{ + tl::equivalence_clusters eq; + + eq.same (1, 1); + EXPECT_EQ (eq2string (eq), "1"); + + eq.same (1, 2); + EXPECT_EQ (eq2string (eq), "1,2"); + + eq.same (3, 4); + EXPECT_EQ (eq2string (eq), "1,2;3,4"); + + eq.same (10, 11); + EXPECT_EQ (eq2string (eq), "1,2;3,4;10,11"); + + eq.same (1, 10); + EXPECT_EQ (eq2string (eq), "1,2,10,11;3,4"); +} + +// apply_other_equivalences +TEST(6_apply_equivalences) +{ + tl::equivalence_clusters eq; + + eq.same (1, 1); + eq.same (2, 2); + eq.same (3, 4); + eq.same (5, 6); + EXPECT_EQ (eq2string (eq), "1;2;3,4;5,6"); + + tl::equivalence_clusters eq2; + + eq2.same (2, 2); + eq2.same (4, 5); + eq2.same (4, 10); + eq2.same (11, 11); + EXPECT_EQ (eq2string (eq2), "2;4,5,10;11"); + + eq.apply_equivalences (eq2); + EXPECT_EQ (eq2string (eq), "1;2;5,6,3,4"); +} + +// merge +TEST(7_merge) +{ + tl::equivalence_clusters eq; + + eq.same (1, 1); + eq.same (2, 2); + eq.same (3, 4); + eq.same (5, 6); + EXPECT_EQ (eq2string (eq), "1;2;3,4;5,6"); + + tl::equivalence_clusters eq2; + + eq2.same (2, 2); + eq2.same (4, 5); + eq2.same (4, 10); + eq2.same (11, 11); + EXPECT_EQ (eq2string (eq2), "2;4,5,10;11"); + + eq.merge (eq2); + EXPECT_EQ (eq2string (eq), "1;2;3,4,5,6,10;11"); +} + diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index bd1680b9f..aa8106e22 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -36,7 +36,8 @@ SOURCES = \ tlInt128Support.cc \ tlLongInt.cc \ tlUniqueIdTests.cc \ - tlListTests.cc + tlListTests.cc \ + tlEquivalenceClustersTests.cc !equals(HAVE_QT, "0") { diff --git a/testdata/algo/device_extract_au1_implicit_nets.gds b/testdata/algo/device_extract_au1_implicit_nets.gds new file mode 100644 index 0000000000000000000000000000000000000000..29b65f48eb4abce9e43f3248fc984a6b60fa85af GIT binary patch literal 9160 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 Date: Mon, 4 Feb 2019 23:43:19 +0100 Subject: [PATCH 238/335] WIP: introduced hierarchical merge and some other hierarchical operations. --- src/db/db/dbAsIfFlatRegion.h | 10 +- src/db/db/dbDeepRegion.cc | 586 ++++++++++++++++++++++++++++++++-- src/db/db/dbDeepRegion.h | 142 +++++++- src/db/db/dbDeepShapeStore.cc | 7 + src/db/db/dbDeepShapeStore.h | 16 +- src/db/db/dbRegion.h | 41 +++ 6 files changed, 762 insertions(+), 40 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index 9353d5d80..c8d5cf92e 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -207,6 +207,11 @@ protected: void update_bbox (const db::Box &box); void invalidate_bbox (); + EdgePairs run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + EdgePairs run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; + RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const; + private: AsIfFlatRegion &operator= (const AsIfFlatRegion &other); @@ -215,11 +220,6 @@ private: virtual db::Box compute_bbox () const; static RegionDelegate *region_from_box (const db::Box &b); - - EdgePairs run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - EdgePairs run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; - RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const; }; } diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 8488a06c8..7cb790e09 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -30,6 +30,10 @@ #include "dbHierProcessor.h" #include "dbCellMapping.h" #include "dbLayoutUtils.h" +#include "dbHierNetworkProcessor.h" +#include "dbCellGraphUtils.h" +#include "dbPolygonTools.h" +#include "tlTimer.h" namespace db { @@ -92,13 +96,13 @@ private: // DeepRegion implementation DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) - : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)), m_merged_polygons (false) + : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)), m_merged_polygons () { init (); } DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics, double area_ratio, size_t max_vertex_count) - : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)), m_merged_polygons (false) + : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)), m_merged_polygons () { init (); @@ -126,16 +130,17 @@ DeepRegion::~DeepRegion () DeepRegion::DeepRegion (const DeepRegion &other) : AsIfFlatRegion (other), m_deep_layer (other.m_deep_layer.copy ()), - m_merged_polygons (other.m_merged_polygons), m_merged_polygons_valid (other.m_merged_polygons_valid) { - // .. nothing yet .. + if (m_merged_polygons_valid) { + m_merged_polygons = other.m_merged_polygons; + } } void DeepRegion::init () { m_merged_polygons_valid = false; - m_merged_polygons.clear (); + m_merged_polygons = db::DeepLayer (); } RegionDelegate * @@ -161,8 +166,7 @@ DeepRegion::begin_merged () const if (! merged_semantics ()) { return begin (); } else { - ensure_merged_polygons_valid (); - return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); + return new DeepRegionIterator (begin_merged_iter ().first); } } @@ -187,10 +191,26 @@ std::pair DeepRegion::begin_merged_iter () const { if (! merged_semantics ()) { + return begin_iter (); + } else { + ensure_merged_polygons_valid (); - return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); + + const db::Layout &layout = m_merged_polygons.layout (); + if (layout.cells () == 0) { + + return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); + + } else { + + const db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + db::RecursiveShapeIterator iter (m_merged_polygons.layout (), top_cell, m_merged_polygons.layer ()); + return std::make_pair (iter, db::ICplxTrans ()); + + } + } } @@ -253,36 +273,126 @@ DeepRegion::less (const Region &other) const } } +namespace { + +class ClusterMerger +{ +public: + ClusterMerger (unsigned int layer, const db::hier_clusters &hc, bool min_coherence, bool report_progress, const std::string &progress_desc) + : m_layer (layer), mp_hc (&hc), m_min_coherence (min_coherence), m_ep (report_progress, progress_desc) + { + // .. nothing yet .. + } + + void set_base_verbosity (int vb) + { + m_ep.set_base_verbosity (vb); + } + + db::Shapes &merged (size_t cid, db::cell_index_type ci, bool initial = true) + { + std::map::iterator s = m_merged_cluster.find (cid); + + // some sanity checks: initial clusters are single-use, are never generated twice and cannot be retrieved again + if (initial) { + tl_assert (s == m_merged_cluster.end ()); + m_done.insert (cid); + } else { + tl_assert (m_done.find (cid) == m_done.end ()); + } + + if (s != m_merged_cluster.end ()) { + return s->second; + } + + s = m_merged_cluster.insert (std::make_pair (cid, db::Shapes ())).first; + + const db::connected_clusters &cc = mp_hc->clusters_per_cell (ci); + const db::local_cluster &c = cc.cluster_by_id (cid); + + std::list > merged_child_clusters; + + const db::connected_clusters::connections_type &conn = cc.connections_for_cluster (cid); + for (db::connected_clusters::connections_type::const_iterator i = conn.begin (); i != conn.end (); ++i) { + const db::Shapes &cc_shapes = merged (i->id (), i->inst ().inst_ptr.cell_index (), false); + merged_child_clusters.push_back (std::make_pair (&cc_shapes, i->inst ().complex_trans ())); + } + + m_ep.clear (); + + size_t pi = 0; + + for (std::list >::const_iterator i = merged_child_clusters.begin (); i != merged_child_clusters.end (); ++i) { + for (db::Shapes::shape_iterator s = i->first->begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + if (s->is_polygon ()) { + db::Polygon poly; + s->polygon (poly); + m_ep.insert (poly.transformed (i->second), pi++); + } + } + } + + for (db::local_cluster::shape_iterator s = c.begin (m_layer); !s.at_end (); ++s) { + db::Polygon poly = s->obj (); + poly.transform (s->trans ()); + m_ep.insert (poly, pi++); + } + + // and run the merge step + db::MergeOp op (0); + db::ShapeGenerator pc (s->second); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_min_coherence); + m_ep.process (pg, op); + + return s->second; + } + +private: + std::map m_merged_cluster; + std::set m_done; + unsigned int m_layer; + const db::hier_clusters *mp_hc; + bool m_min_coherence; + db::EdgeProcessor m_ep; +}; + +} + void DeepRegion::ensure_merged_polygons_valid () const { if (! m_merged_polygons_valid) { - m_merged_polygons.clear (); + m_merged_polygons = m_deep_layer.new_layer (); - db::EdgeProcessor ep (report_progress (), progress_desc ()); + tl::SelfTimer timer (tl::verbosity () > base_verbosity (), "Ensure merged polygons"); + + db::Layout &layout = const_cast (m_deep_layer.layout ()); + + db::hier_clusters hc; + db::Connectivity conn; + conn.connect (m_deep_layer); + // TODO: this uses the wrong verbosity inside ... + hc.build (layout, m_deep_layer.initial_cell (), db::ShapeIterator::Polygons, conn); + + // collect the clusters and merge them into big polygons + // NOTE: using the ClusterMerger we merge bottom-up forming bigger and bigger polygons. This is + // hopefully more efficient that collecting everything and will lead to reuse of parts. + + ClusterMerger cm (m_deep_layer.layer (), hc, min_coherence (), report_progress (), progress_desc ()); if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); + cm.set_base_verbosity (base_verbosity ()); } - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + const db::connected_clusters &cc = hc.clusters_per_cell (c->cell_index ()); + for (db::connected_clusters::const_iterator cl = cc.begin (); cl != cc.end (); ++cl) { + if (cc.is_root (cl->id ())) { + db::Shapes &s = cm.merged (cl->id (), c->cell_index ()); + c->shapes (m_merged_polygons.layer ()).swap (s); + } + } } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (0); - db::ShapeGenerator pc (m_merged_polygons); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); - ep.process (pg, op); m_merged_polygons_valid = true; @@ -470,4 +580,424 @@ DeepRegion::add (const Region &other) const } } +static int is_box_from_iter (db::RecursiveShapeIterator i) +{ + if (i.at_end ()) { + return -1; + } + + if (i->is_box ()) { + ++i; + if (i.at_end ()) { + return 1; + } + } else if (i->is_path () || i->is_polygon ()) { + db::Polygon poly; + i->polygon (poly); + if (poly.is_box ()) { + ++i; + if (i.at_end ()) { + return 1; + } + } + } else { + return -1; + } + + return 0; // undecided +} + +bool +DeepRegion::is_box () const +{ + int f = is_box_from_iter (begin_iter ().first); + if (f != 0) { + return f > 0; + } + + // fallback: merge and then look again + return is_box_from_iter (begin_merged_iter ().first) > 0; +} + +size_t +DeepRegion::size () const +{ + size_t n = 0; + + const db::Layout &layout = m_deep_layer.layout (); + db::CellCounter cc (&layout); + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + n += cc.weight (*c) * layout.cell (*c).shapes (m_deep_layer.layer ()).size (); + } + + return n; +} + +DeepRegion::area_type +DeepRegion::area (const db::Box &box) const +{ + if (box.empty ()) { + + ensure_merged_polygons_valid (); + + // @@@ scaled instances! + + DeepRegion::area_type a = 0; + + const db::Layout &layout = m_merged_polygons.layout (); + db::CellCounter cc (&layout); + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + DeepRegion::area_type ac = 0; + for (db::ShapeIterator s = layout.cell (*c).shapes (m_merged_polygons.layer ()).begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + ac += s->area (); + } + a += cc.weight (*c) * ac; + } + + return a; + + } else { + return db::AsIfFlatRegion::area (box); + } +} + +DeepRegion::perimeter_type +DeepRegion::perimeter (const db::Box &box) const +{ + if (box.empty ()) { + + ensure_merged_polygons_valid (); + + // @@@ scaled instances! + + DeepRegion::perimeter_type p = 0; + + const db::Layout &layout = m_merged_polygons.layout (); + db::CellCounter cc (&layout); + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + DeepRegion::perimeter_type pc = 0; + for (db::ShapeIterator s = layout.cell (*c).shapes (m_merged_polygons.layer ()).begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + pc += s->perimeter (); + } + p += cc.weight (*c) * pc; + } + + return p; + + } else { + return db::AsIfFlatRegion::perimeter (box); + } +} + +Box +DeepRegion::bbox () const +{ + return m_deep_layer.initial_cell ().bbox (m_deep_layer.layer ()); +} + +std::string +DeepRegion::to_string (size_t nmax) const +{ + return db::AsIfFlatRegion::to_string (nmax); +} + +EdgePairs +DeepRegion::grid_check (db::Coord gx, db::Coord gy) const +{ + // NOTE: snap be optimized by forming grid variants etc. + return db::AsIfFlatRegion::grid_check (gx, gy); +} + +EdgePairs +DeepRegion::angle_check (double min, double max, bool inverse) const +{ + // NOTE: snap be optimized by forming rotation variants etc. + return db::AsIfFlatRegion::angle_check (min, max, inverse); +} + +RegionDelegate * +DeepRegion::snapped (db::Coord gx, db::Coord gy) +{ + // NOTE: snap be optimized by forming grid variants etc. + return db::AsIfFlatRegion::snapped (gx, gy); +} + +Edges +DeepRegion::edges (const EdgeFilterBase *filter) const +{ + // NOTE: needs a deep edge set for optimizing for hierarchy. + // At least the length filter is easy to optimize in the hierarchical case. + return db::AsIfFlatRegion::edges (filter); +} + +RegionDelegate * +DeepRegion::filtered (const PolygonFilterBase &filter) const +{ + if (filter.isotropic ()) { + + ensure_merged_polygons_valid (); + + // @@@ scaled instances! + + db::Layout &layout = m_merged_polygons.layout (); + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.new_layer ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (m_merged_polygons.layer ()); + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + if (filter.selected (poly)) { + st.insert (poly); + } + } + + } + + return res.release (); + + } else { + return db::AsIfFlatRegion::filtered (filter); + } +} + +RegionDelegate * +DeepRegion::merged_in_place () +{ + ensure_merged_polygons_valid (); + + // NOTE: this makes both layers share the same resource + m_deep_layer = m_merged_polygons; + + return this; +} + +RegionDelegate * +DeepRegion::merged_in_place (bool min_coherence, unsigned int min_wc) +{ + // TODO: can probably be optimized + return db::AsIfFlatRegion::merged_in_place (min_coherence, min_wc); +} + +RegionDelegate * +DeepRegion::merged () const +{ + ensure_merged_polygons_valid (); + + db::Layout &layout = const_cast (m_merged_polygons.layout ()); + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.new_layer ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + c->shapes (res->deep_layer ().layer ()) = c->shapes (m_merged_polygons.layer ()); + } + + res->deep_layer ().layer (); + + return res.release (); +} + +RegionDelegate * +DeepRegion::merged (bool min_coherence, unsigned int min_wc) const +{ + // TODO: can probably be optimized + return db::AsIfFlatRegion::merged (min_coherence, min_wc); +} + +RegionDelegate * +DeepRegion::strange_polygon_check () const +{ + // TODO: can probably be optimized + return db::AsIfFlatRegion::strange_polygon_check (); +} + +RegionDelegate * +DeepRegion::sized (coord_type d, unsigned int mode) const +{ + ensure_merged_polygons_valid (); + + db::Layout &layout = m_merged_polygons.layout (); + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.new_layer ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (m_merged_polygons.layer ()); + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + db::ShapeGenerator pc (st, false /*no clear - already empty*/); + db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); + db::SizingPolygonFilter siz (pg2, d, d, mode); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + siz.put (poly); + } + + } + + return res.release (); +} + +RegionDelegate * +DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const +{ + if (dx == dy) { + return sized (dx, mode); + } else { + return db::AsIfFlatRegion::sized (dx, dy, mode); + } +} + +RegionDelegate * +DeepRegion::holes () const +{ + ensure_merged_polygons_valid (); + + db::Layout &layout = m_merged_polygons.layout (); + + std::vector pts; + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.new_layer ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (m_merged_polygons.layer ()); + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + for (size_t i = 0; i < si->holes (); ++i) { + db::Polygon h; + pts.clear (); + for (db::Shape::point_iterator p = si->begin_hole ((unsigned int) i); p != si->end_hole ((unsigned int) i); ++p) { + pts.push_back (*p); + } + h.assign_hull (pts.begin (), pts.end ()); + st.insert (h); + } + } + + } + + return res.release (); +} + +RegionDelegate * +DeepRegion::hulls () const +{ + ensure_merged_polygons_valid (); + + db::Layout &layout = m_merged_polygons.layout (); + + std::vector pts; + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.new_layer ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (m_merged_polygons.layer ()); + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon h; + pts.clear (); + for (db::Shape::point_iterator p = si->begin_hull (); p != si->end_hull (); ++p) { + pts.push_back (*p); + } + h.assign_hull (pts.begin (), pts.end ()); + st.insert (h); + } + + } + + return res.release (); +} + +RegionDelegate * +DeepRegion::in (const Region &other, bool invert) const +{ + // TODO: this can be optimized maybe ... + return db::AsIfFlatRegion::in (other, invert); +} + +RegionDelegate * +DeepRegion::rounded_corners (double rinner, double router, unsigned int n) const +{ + ensure_merged_polygons_valid (); + + // @@@ scaled instances + + db::Layout &layout = m_merged_polygons.layout (); + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.new_layer ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (m_merged_polygons.layer ()); + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + st.insert (db::compute_rounded (poly, rinner, router, n)); + } + + } + + return res.release (); +} + +RegionDelegate * +DeepRegion::smoothed (coord_type d) const +{ + ensure_merged_polygons_valid (); + + // @@@ scaled instances + + db::Layout &layout = m_merged_polygons.layout (); + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.new_layer ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (m_merged_polygons.layer ()); + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + st.insert (db::smooth (poly, d)); + } + + } + + return res.release (); +} + +EdgePairs +DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + // TODO: implement hierarchically + return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); +} + +EdgePairs +DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + // TODO: implement hierarchically + return db::AsIfFlatRegion::run_single_polygon_check (rel, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); +} + +RegionDelegate * +DeepRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const +{ + // TODO: implement hierarchically + return db::AsIfFlatRegion::selected_interacting_generic (other, mode, touching, inverse); +} + +RegionDelegate * +DeepRegion::selected_interacting_generic (const Edges &other, bool inverse) const +{ + // TODO: implement hierarchically + return db::AsIfFlatRegion::selected_interacting_generic (other, inverse); +} + } diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 720782399..df214e093 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -78,6 +78,141 @@ public: virtual RegionDelegate *add_in_place (const Region &other); virtual RegionDelegate *add (const Region &other) const; + virtual bool is_box () const; + virtual size_t size () const; + + virtual area_type area (const db::Box &box) const; + virtual perimeter_type perimeter (const db::Box &box) const; + virtual Box bbox () const; + + virtual std::string to_string (size_t nmax) const; + + EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const; + virtual EdgePairs angle_check (double min, double max, bool inverse) const; + + virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) + { + return snapped (gx, gy); + } + + virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); + + virtual Edges edges (const EdgeFilterBase *) const; + + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) + { + return filtered (filter); + } + + virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const; + + virtual RegionDelegate *merged_in_place (); + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); + + virtual RegionDelegate *merged () const; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const; + + virtual RegionDelegate *strange_polygon_check () const; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; + + virtual RegionDelegate *selected_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, false); + } + + virtual RegionDelegate *selected_not_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, true); + } + + virtual RegionDelegate *selected_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, false); + } + + virtual RegionDelegate *selected_not_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, true); + } + + virtual RegionDelegate *selected_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, false); + } + + virtual RegionDelegate *selected_not_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, true); + } + + virtual RegionDelegate *selected_interacting (const Edges &other) const + { + return selected_interacting_generic (other, false); + } + + virtual RegionDelegate *selected_not_interacting (const Edges &other) const + { + return selected_interacting_generic (other, true); + } + + virtual RegionDelegate *selected_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, false); + } + + virtual RegionDelegate *selected_not_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, true); + } + + virtual RegionDelegate *holes () const; + virtual RegionDelegate *hulls () const; + virtual RegionDelegate *in (const Region &other, bool invert) const; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; + virtual RegionDelegate *smoothed (coord_type d) const; + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; const DeepLayer &deep_layer () const @@ -97,14 +232,17 @@ private: DeepRegion &operator= (const DeepRegion &other); DeepLayer m_deep_layer; - // TODO: have hierarchical merged polygons later - mutable db::Shapes m_merged_polygons; + mutable DeepLayer m_merged_polygons; mutable bool m_merged_polygons_valid; void init (); void ensure_merged_polygons_valid () const; DeepLayer and_or_not_with(const DeepRegion *other, bool and_op) const; void add_from (const DeepLayer &dl); + EdgePairs run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + EdgePairs run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; + RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const; }; } diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index d05ab60f8..b6b1af6bd 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -141,6 +141,13 @@ bool DeepLayer::operator== (const DeepLayer &other) const return true; } +DeepLayer +DeepLayer::new_layer() const +{ + db::DeepShapeStore *store = const_cast (mp_store.get ()); + return DeepLayer (store, m_layout, store->layout (m_layout).insert_layer ()); +} + db::Layout & DeepLayer::layout () { diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index f8dac096b..e1d67a337 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -61,6 +61,12 @@ public: */ ~DeepLayer (); + /** + * @brief The constructor from the detailed information + * Use this constructor if you know what you're doing. + */ + DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer); + /** * @brief Conversion operator from Region to DeepLayer * This requires the Region to be a DeepRegion. Otherwise, this constructor will assert @@ -87,6 +93,11 @@ public: */ bool operator== (const DeepLayer &other) const; + /** + * @brief Creates a new empty layer based on this one + */ + DeepLayer new_layer () const; + /** * @brief Gets the layout object * The return value is guaranteed to be non-null. @@ -156,11 +167,6 @@ public: private: friend class DeepShapeStore; - /** - * @brief The constructor - */ - DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer); - void check_dss () const; tl::weak_ptr mp_store; diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 225bb7d72..77da2d894 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -50,6 +50,7 @@ public: virtual ~PolygonFilterBase () { } virtual bool selected (const db::Polygon &polgon) const = 0; + virtual bool isotropic () const = 0; }; /** @@ -96,6 +97,14 @@ struct DB_PUBLIC RegionPerimeterFilter } } + /** + * @brief This filter is isotropic + */ + virtual bool isotropic () const + { + return true; + } + private: perimeter_type m_pmin, m_pmax; bool m_inverse; @@ -141,6 +150,14 @@ struct DB_PUBLIC RegionAreaFilter } } + /** + * @brief This filter is isotropic + */ + virtual bool isotropic () const + { + return true; + } + private: area_type m_amin, m_amax; bool m_inverse; @@ -173,6 +190,14 @@ struct DB_PUBLIC RectilinearFilter return poly.is_rectilinear () != m_inverse; } + /** + * @brief This filter is isotropic + */ + virtual bool isotropic () const + { + return true; + } + private: bool m_inverse; }; @@ -204,6 +229,14 @@ struct DB_PUBLIC RectangleFilter return poly.is_box () != m_inverse; } + /** + * @brief This filter is isotropic + */ + virtual bool isotropic () const + { + return true; + } + private: bool m_inverse; }; @@ -278,6 +311,14 @@ struct DB_PUBLIC RegionBBoxFilter } } + /** + * @brief This filter is isotropic unless the parameter is width or height + */ + virtual bool isotropic () const + { + return m_parameter != BoxWidth && m_parameter != BoxHeight; + } + private: value_type m_vmin, m_vmax; bool m_inverse; From 7c043dbb99eca46c6636c21f4d8b4aba0b3f3ba7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 5 Feb 2019 00:25:32 +0100 Subject: [PATCH 239/335] WIP: fixed DeepRegion implementation somewhat. Needs testing. --- src/db/db/dbDeepRegion.cc | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 7cb790e09..a3d606325 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -291,21 +291,21 @@ public: db::Shapes &merged (size_t cid, db::cell_index_type ci, bool initial = true) { - std::map::iterator s = m_merged_cluster.find (cid); + std::map, db::Shapes>::iterator s = m_merged_cluster.find (std::make_pair (cid, ci)); // some sanity checks: initial clusters are single-use, are never generated twice and cannot be retrieved again if (initial) { tl_assert (s == m_merged_cluster.end ()); - m_done.insert (cid); + m_done.insert (std::make_pair (cid, ci)); } else { - tl_assert (m_done.find (cid) == m_done.end ()); + tl_assert (m_done.find (std::make_pair (cid, ci)) == m_done.end ()); } if (s != m_merged_cluster.end ()) { return s->second; } - s = m_merged_cluster.insert (std::make_pair (cid, db::Shapes ())).first; + s = m_merged_cluster.insert (std::make_pair (std::make_pair (cid, ci), db::Shapes (false))).first; const db::connected_clusters &cc = mp_hc->clusters_per_cell (ci); const db::local_cluster &c = cc.cluster_by_id (cid); @@ -348,8 +348,8 @@ public: } private: - std::map m_merged_cluster; - std::set m_done; + std::map, db::Shapes> m_merged_cluster; + std::set > m_done; unsigned int m_layer; const db::hier_clusters *mp_hc; bool m_min_coherence; @@ -384,11 +384,12 @@ DeepRegion::ensure_merged_polygons_valid () const cm.set_base_verbosity (base_verbosity ()); } + // @@@ iterate only over the called cells? for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { const db::connected_clusters &cc = hc.clusters_per_cell (c->cell_index ()); - for (db::connected_clusters::const_iterator cl = cc.begin (); cl != cc.end (); ++cl) { - if (cc.is_root (cl->id ())) { - db::Shapes &s = cm.merged (cl->id (), c->cell_index ()); + for (db::connected_clusters::all_iterator cl = cc.begin_all (); ! cl.at_end (); ++cl) { + if (cc.is_root (*cl)) { + db::Shapes &s = cm.merged (*cl, c->cell_index ()); c->shapes (m_merged_polygons.layer ()).swap (s); } } @@ -583,13 +584,13 @@ DeepRegion::add (const Region &other) const static int is_box_from_iter (db::RecursiveShapeIterator i) { if (i.at_end ()) { - return -1; + return true; } if (i->is_box ()) { ++i; if (i.at_end ()) { - return 1; + return true; } } else if (i->is_path () || i->is_polygon ()) { db::Polygon poly; @@ -597,26 +598,18 @@ static int is_box_from_iter (db::RecursiveShapeIterator i) if (poly.is_box ()) { ++i; if (i.at_end ()) { - return 1; + return true; } } - } else { - return -1; } - return 0; // undecided + return false; } bool DeepRegion::is_box () const { - int f = is_box_from_iter (begin_iter ().first); - if (f != 0) { - return f > 0; - } - - // fallback: merge and then look again - return is_box_from_iter (begin_merged_iter ().first) > 0; + return is_box_from_iter (begin_iter ().first); } size_t From decc5ede136bfc57cf244b161344b2fb664856b1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 5 Feb 2019 23:39:31 +0100 Subject: [PATCH 240/335] Robustification of Region - Tests for merge - Locking the layout when writing back the data for performance improvement --- src/db/db/dbAsIfFlatRegion.cc | 31 ++---- src/db/db/dbDeepRegion.cc | 11 +- src/db/db/dbDeepShapeStore.cc | 2 + src/db/db/dbFlatRegion.cc | 8 +- src/db/db/dbOriginalLayerRegion.cc | 4 +- src/db/db/dbRegionDelegate.cc | 2 +- src/db/unit_tests/dbDeepRegionTests.cc | 129 ++++++++++++++++++++++++ testdata/algo/deep_region_au7.gds | Bin 0 -> 7834 bytes testdata/algo/vexriscv_clocked_r.oas.gz | Bin 0 -> 883924 bytes 9 files changed, 149 insertions(+), 38 deletions(-) create mode 100644 testdata/algo/deep_region_au7.gds create mode 100644 testdata/algo/vexriscv_clocked_r.oas.gz diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 3e97ea64d..20be32979 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -458,9 +458,7 @@ RegionDelegate * AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const { db::EdgeProcessor ep (report_progress (), progress_desc ()); - if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); - } + ep.set_base_verbosity (base_verbosity ()); // shortcut if (empty ()) { @@ -1058,9 +1056,7 @@ AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc) const } else { db::EdgeProcessor ep (report_progress (), progress_desc ()); - if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); - } + ep.set_base_verbosity (base_verbosity ()); // count edges and reserve memory size_t n = 0; @@ -1138,9 +1134,7 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const // Generic case - the size operation will merge first db::EdgeProcessor ep (report_progress (), progress_desc ()); - if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); - } + ep.set_base_verbosity (base_verbosity ()); // count edges and reserve memory size_t n = 0; @@ -1222,9 +1216,7 @@ AsIfFlatRegion::and_with (const Region &other) const // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); - if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); - } + ep.set_base_verbosity (base_verbosity ()); // count edges and reserve memory size_t n = 0; @@ -1279,9 +1271,7 @@ AsIfFlatRegion::not_with (const Region &other) const // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); - if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); - } + ep.set_base_verbosity (base_verbosity ()); // count edges and reserve memory size_t n = 0; @@ -1334,9 +1324,7 @@ AsIfFlatRegion::xor_with (const Region &other) const // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); - if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); - } + ep.set_base_verbosity (base_verbosity ()); // count edges and reserve memory size_t n = 0; @@ -1390,9 +1378,7 @@ AsIfFlatRegion::or_with (const Region &other) const // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); - if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); - } + ep.set_base_verbosity (base_verbosity ()); // count edges and reserve memory size_t n = 0; @@ -1468,6 +1454,9 @@ AsIfFlatRegion::add (const Region &other) const void AsIfFlatRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const { + // improves performance when inserting an original layout into the same layout + db::LayoutLocker locker (layout); + db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); for (RegionIterator p (begin ()); ! p.at_end (); ++p) { shapes.insert (*p); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index a3d606325..61b0fbec5 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -380,9 +380,7 @@ DeepRegion::ensure_merged_polygons_valid () const // hopefully more efficient that collecting everything and will lead to reuse of parts. ClusterMerger cm (m_deep_layer.layer (), hc, min_coherence (), report_progress (), progress_desc ()); - if (base_verbosity ()) { - cm.set_base_verbosity (base_verbosity ()); - } + cm.set_base_verbosity (base_verbosity ()); // @@@ iterate only over the called cells? for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { @@ -390,7 +388,8 @@ DeepRegion::ensure_merged_polygons_valid () const for (db::connected_clusters::all_iterator cl = cc.begin_all (); ! cl.at_end (); ++cl) { if (cc.is_root (*cl)) { db::Shapes &s = cm.merged (*cl, c->cell_index ()); - c->shapes (m_merged_polygons.layer ()).swap (s); + c->shapes (m_merged_polygons.layer ()).insert (s); + s.clear (); // not needed anymore } } } @@ -461,9 +460,7 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const db::BoolAndOrNotLocalOperation op (and_op); db::LocalProcessor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ()); - if (base_verbosity ()) { - proc.set_base_verbosity (base_verbosity ()); - } + proc.set_base_verbosity (base_verbosity ()); proc.set_threads (m_deep_layer.store ()->threads ()); proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ()); proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index b6b1af6bd..97a35de83 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -468,6 +468,8 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout void DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) { + db::LayoutLocker locker (into_layout); + const db::Layout &source_layout = deep_layer.layout (); if (source_layout.begin_top_down () == source_layout.end_top_cells ()) { // empty source - nothing to do. diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index d3eb99105..c4c8d51b3 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -107,9 +107,7 @@ FlatRegion::ensure_merged_polygons_valid () const m_merged_polygons.clear (); db::EdgeProcessor ep (report_progress (), progress_desc ()); - if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); - } + ep.set_base_verbosity (base_verbosity ()); // count edges and reserve memory size_t n = 0; @@ -246,9 +244,7 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi invalidate_cache (); db::EdgeProcessor ep (report_progress (), progress_desc ()); - if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); - } + ep.set_base_verbosity (base_verbosity ()); // count edges and reserve memory size_t n = 0; diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc index 45812d34d..d481c5500 100644 --- a/src/db/db/dbOriginalLayerRegion.cc +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -253,9 +253,7 @@ OriginalLayerRegion::ensure_merged_polygons_valid () const m_merged_polygons.clear (); db::EdgeProcessor ep (report_progress (), progress_desc ()); - if (base_verbosity ()) { - ep.set_base_verbosity (base_verbosity ()); - } + ep.set_base_verbosity (base_verbosity ()); // count edges and reserve memory size_t n = 0; diff --git a/src/db/db/dbRegionDelegate.cc b/src/db/db/dbRegionDelegate.cc index fc8381ca9..0e6328ce7 100644 --- a/src/db/db/dbRegionDelegate.cc +++ b/src/db/db/dbRegionDelegate.cc @@ -30,7 +30,7 @@ namespace db RegionDelegate::RegionDelegate () { - m_base_verbosity = 0; + m_base_verbosity = 30; m_report_progress = false; m_merged_semantics = true; m_strict_handling = false; diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 720708876..4a202c90d 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -339,3 +339,132 @@ TEST(6_Reduction) db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au6.gds"); } +TEST(7_Merge) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6), dss); + + db::Region r6_merged = r6.merged (); + db::Region r6_merged_minwc = r6.merged (false, 1); + + db::Region r6_minwc = r6; + r6_minwc.merge (false, 1); + + r6.merge (); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r6); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r6_minwc); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r6_merged); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r6_merged_minwc); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au7.gds"); +} + + + +TEST(100_Integration) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/vexriscv_clocked_r.oas.gz"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l4 = ly.get_layer (db::LayerProperties (4, 0)); + unsigned int l5 = ly.get_layer (db::LayerProperties (5, 0)); + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + unsigned int l7 = ly.get_layer (db::LayerProperties (7, 0)); + unsigned int l10 = ly.get_layer (db::LayerProperties (10, 0)); + unsigned int l11 = ly.get_layer (db::LayerProperties (11, 0)); + unsigned int l14 = ly.get_layer (db::LayerProperties (14, 0)); + unsigned int l16 = ly.get_layer (db::LayerProperties (16, 0)); + unsigned int l18 = ly.get_layer (db::LayerProperties (18, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r4 (db::RecursiveShapeIterator (ly, top_cell, l4), dss); + db::Region r5 (db::RecursiveShapeIterator (ly, top_cell, l5), dss); + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6), dss); + db::Region r7 (db::RecursiveShapeIterator (ly, top_cell, l7), dss); + db::Region r10 (db::RecursiveShapeIterator (ly, top_cell, l10), dss); + db::Region r11 (db::RecursiveShapeIterator (ly, top_cell, l11), dss); + db::Region r14 (db::RecursiveShapeIterator (ly, top_cell, l14), dss); + db::Region r16 (db::RecursiveShapeIterator (ly, top_cell, l16), dss); + db::Region r18 (db::RecursiveShapeIterator (ly, top_cell, l18), dss); + + db::Region psd = r4 - r7; + db::Region nsd = r3 - r7; + db::Region pgate = r4 & r7; + db::Region ngate = r3 & r7; + db::Region poly_cont = r10 & r7; + db::Region diff_cont = r10 - r7; + + r1.merge (); + r3.merge (); + r4.merge (); + r5.merge (); + r6.merge (); + r7.merge (); + r10.merge (); + r11.merge (); + r14.merge (); + r16.merge (); + r18.merge (); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (4, 0)), r4); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (5, 0)), r5); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (6, 0)), r6); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (7, 0)), r7); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r10); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r11); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r14); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (16, 0)), r16); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (18, 0)), r18); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (100, 0)), psd); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (101, 0)), nsd); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (102, 0)), pgate); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (103, 0)), ngate); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (104, 0)), poly_cont); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (105, 0)), diff_cont); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au100.gds"); +} diff --git a/testdata/algo/deep_region_au7.gds b/testdata/algo/deep_region_au7.gds new file mode 100644 index 0000000000000000000000000000000000000000..2c7a8f312fb14d4fc7d28db94b35640b03d97e41 GIT binary patch literal 7834 zcmc(iKTKO!6vi*Mc?QR%4xvibSQJGGN+c>MMU6^BR4OBaf(#)}M3Ad8WX!<8$iR@L zQicxM%D@n*3qyts9T*r;Cx#4+86q`f28J%JXx#Jp+@t5?a&Pdds#roFI_LiGIevck z&*u`C$t=5aS0?|v%eZcL!Ij)ct^d28OfoRsn@c^4holO%K1`{OQGaIVlVRI!w8G~TT_d~)aL@8oRZ=!9(YY1gK6 zG=p+^w;2ZfP@| ztG`ThC#{e_>$EM@8VzWEx6$}(KUIm-3T<6sXZ#!9d;^+02(2*eAYB{0L$LA+<9wJm ztk9PFv?C1B_ruOW+T3AihY*!GX`p}l?$k4OsDySWaQQc)693l@q3ZW;@wnbC=Fa3q zB_8Md*0_Ht>Yu%wD*E^NVE?59u2VW-u75&Q;_>?D-}1QgZ<+hJCMxkbe_@aNhob*a z)>B3QJ|FD=Vwvk)EHhW8-vaZL>7Byw;`2d%@CJ_?yusXO&qO8O)F6MU#Qj52_tJi< z=-=mq{1tz~U7^zf{P3YD*Z27#fAtBEyZVH=Yk!DJJl_AvD)$dX{m-dqQS|Tg!TO`S zTxWEbxi2n@N<3bF{1uNIf5qHHMO5N({;TKQKNNjmf0!!z_xWJ|lSQsGS!C{;by11O z>rajFxTz85Zax*2c$}Z6_R%L4{ZGG0RpO+<{wsB^Q>imIt!BSFEjuBV^N96`5%tBe<=E2eU>Wv z_xV8obnd>UvyX1((A*ZEzoHV)PK4jT{1|vT_egQm$N48J@fd$@lX)mOoIj%AeLm1X z?e8y{hl0cYFACo0gZ%Ii^H6ZZhpB@1`5-^G#yk`}_CHbZJ|E;K_n3!*!~P=*-sgk- zOo@3YIIMqB@ID{p7gm{vg2Va~1@H4g{=swRq2TcTi-Pz00ME`yoRBypQHi%Xq_+DL z902`MZE(0hiAp@iq@dp2*c!PgYyurUH z-r!#pZ}2aQH~1ID8~lsn4gN*(2LGaXgMU%H_a{xCg8SPk{EOlZ{zdTy|Dt$|1b8?ZCe% z-r!#pZ}2aQH~1ID8~lsnC-?aN55>Lz6#hl=2LGb?h1HJzi{cIbrG+>7mlmGxuJp`s tPwl|JDBj>-6mRe^iZ}Qd#T)#K;tl>q@dp2*c!PgYd?)@TPfIN4{sGUgN)-SA literal 0 HcmV?d00001 diff --git a/testdata/algo/vexriscv_clocked_r.oas.gz b/testdata/algo/vexriscv_clocked_r.oas.gz new file mode 100644 index 0000000000000000000000000000000000000000..0ecd4f4f22894d450b90a01d72029a57aa9fdb69 GIT binary patch literal 883924 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≶j|vtePQPOVaMysrQKg~>b_=O;2SxI^HodQ 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